diff --git a/krita/benchmarks/kis_bcontrast_benchmark.cpp b/krita/benchmarks/kis_bcontrast_benchmark.cpp index 8063bcc4b4..52f26067a6 100644 --- a/krita/benchmarks/kis_bcontrast_benchmark.cpp +++ b/krita/benchmarks/kis_bcontrast_benchmark.cpp @@ -1,99 +1,98 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kis_bcontrast_benchmark.h" #include "kis_benchmark_values.h" -#include "kis_paint_device.h" #include #include #include #include #include "filter/kis_filter_registry.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter.h" #include "kis_processing_information.h" #include "kis_selection.h" #include #include "krita_utils.h" void KisBContrastBenchmark::initTestCase() { m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_device = new KisPaintDevice(m_colorSpace); m_color = KoColor(m_colorSpace); srand(31524744); int r,g,b; KisSequentialIterator it(m_device, QRect(0, 0, GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT)); do { r = rand() % 255; g = rand() % 255; b = rand() % 255; m_color.fromQColor(QColor(r,g,b)); memcpy(it.rawData(), m_color.data(), m_colorSpace->pixelSize()); } while (it.nextPixel()); } void KisBContrastBenchmark::cleanupTestCase() { } void KisBContrastBenchmark::benchmarkFilter() { KisFilterSP filter = KisFilterRegistry::instance()->value("brightnesscontrast"); KisFilterConfiguration * kfc = filter->defaultConfiguration(m_device); // Get the predefined configuration from a file QFile file(QString(FILES_DATA_DIR) + QDir::separator() + filter->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); kfc->fromXML(s); } QSize size = KritaUtils::optimalPatchSize(); QVector rects = KritaUtils::splitRectIntoPatches(QRect(0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT), size); QBENCHMARK{ foreach(const QRect &rc, rects) { filter->process(m_device, rc, kfc); } } } QTEST_MAIN(KisBContrastBenchmark) diff --git a/krita/benchmarks/kis_bcontrast_benchmark.h b/krita/benchmarks/kis_bcontrast_benchmark.h index 7a52be8c6b..7770015abf 100644 --- a/krita/benchmarks/kis_bcontrast_benchmark.h +++ b/krita/benchmarks/kis_bcontrast_benchmark.h @@ -1,49 +1,47 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BCONTRAST_BENCHMARK_H #define KIS_BCONTRAST_BENCHMARK_H #include #include #include #include -class KisPaintDevice; -class KoColorSpace; class KoColor; class KisBContrastBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KoColor m_color; KisPaintDeviceSP m_device; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkFilter(); }; #endif diff --git a/krita/benchmarks/kis_blur_benchmark.cpp b/krita/benchmarks/kis_blur_benchmark.cpp index 293bddd0fd..789fcde173 100644 --- a/krita/benchmarks/kis_blur_benchmark.cpp +++ b/krita/benchmarks/kis_blur_benchmark.cpp @@ -1,91 +1,90 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "kis_blur_benchmark.h" #include "kis_benchmark_values.h" -#include "kis_paint_device.h" #include #include #include #include #include "filter/kis_filter_registry.h" #include "filter/kis_filter_configuration.h" #include "filter/kis_filter.h" #include "kis_processing_information.h" #include "kis_selection.h" #include void KisBlurBenchmark::initTestCase() { m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_device = new KisPaintDevice(m_colorSpace); m_color = KoColor(m_colorSpace); QColor qcolor(Qt::red); srand(31524744); int r,g,b; KisSequentialIterator it(m_device, QRect(0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT)); do { r = rand() % 255; g = rand() % 255; b = rand() % 255; m_color.fromQColor(QColor(r,g,b)); memcpy(it.rawData(), m_color.data(), m_colorSpace->pixelSize()); } while (it.nextPixel()); } void KisBlurBenchmark::cleanupTestCase() { } void KisBlurBenchmark::benchmarkFilter() { KisFilterSP filter = KisFilterRegistry::instance()->value("blur"); KisFilterConfiguration * kfc = filter->defaultConfiguration(m_device); // Get the predefined configuration from a file QFile file(QString(FILES_DATA_DIR) + QDir::separator() + filter->id() + ".cfg"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out << kfc->toXML(); } else { QString s; QTextStream in(&file); s = in.readAll(); kfc->fromXML(s); } QBENCHMARK{ filter->process(m_device, QRect(0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT), kfc); } } QTEST_MAIN(KisBlurBenchmark) diff --git a/krita/benchmarks/kis_blur_benchmark.h b/krita/benchmarks/kis_blur_benchmark.h index 0c96a48c1f..ebe18df055 100644 --- a/krita/benchmarks/kis_blur_benchmark.h +++ b/krita/benchmarks/kis_blur_benchmark.h @@ -1,47 +1,45 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BLUR_BENCHMARK_H #define KIS_BLUR_BENCHMARK_H #include #include #include #include -class KisPaintDevice; -class KoColorSpace; class KoColor; class KisBlurBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KisPaintDeviceSP m_device; KoColor m_color; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkFilter(); }; #endif diff --git a/krita/benchmarks/kis_filter_selections_benchmark.cpp b/krita/benchmarks/kis_filter_selections_benchmark.cpp index d8ea9995ec..7c78cc8a1d 100644 --- a/krita/benchmarks/kis_filter_selections_benchmark.cpp +++ b/krita/benchmarks/kis_filter_selections_benchmark.cpp @@ -1,294 +1,293 @@ /* * Copyright (c) 2009 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_filter_selections_benchmark.h" #include "kis_painter.h" #include #include "testutil.h" -#include "kis_selection.h" #include "kis_transaction.h" #include #include "kis_datamanager.h" #define NUM_CYCLES 50 #define WARMUP_CYCLES 2 #define SHOW_WARMUPS 0 /** * Our filters don't know anything about applyAlphaU8Mask * That's why they treat semy-selected pixels badly * * If you have a hack in KisFilter - processSpecial(..) method * that takes into account this alpha mask then activate * the following define */ #define USE_GOOD_SELECTIONS 0 #define USE_UTIME 0 #if(USE_UTIME==1) #include class KisTimeCounter { public: KisTimeCounter() { m_factor = double(sysconf(_SC_CLK_TCK)) / 1000.0; restart(); } void restart() { times(&m_startTime); } double elapsed() { struct tms endTime; times(&endTime); return double(endTime.tms_utime - m_startTime.tms_utime) / m_factor; } private: struct tms m_startTime; double m_factor; }; #else /* if(USE_UTIME==0) */ typedef QTime KisTimeCounter; #endif void KisFilterSelectionsBenchmark::initSelection() { m_selection = new KisSelection(); KisPixelSelectionSP pixelSelection = m_selection->pixelSelection(); //67.2% deselected dbgKrita << "Deselected: 67.2%"; pixelSelection->dataManager()->clear(75, 75, 500, 320, 255); pixelSelection->dataManager()->clear(100, 100, 50, 50, quint8(0)); pixelSelection->dataManager()->clear(150, 150, 50, 50, quint8(0)); pixelSelection->dataManager()->clear(200, 200, 50, 50, quint8(0)); pixelSelection->dataManager()->clear(375, 195, 200, 200, quint8(0)); pixelSelection->dataManager()->clear(75, 195, 200, 200, quint8(0)); pixelSelection->dataManager()->clear(375, 75, 150, 150, quint8(0)); pixelSelection->dataManager()->clear(205, 105, 50, 50, quint8(128)); // 94.9% deselected // dbgKrita << "Deselected: 94.9%"; // pixelSelection->dataManager()->clear(75,75,500,320,255); // pixelSelection->dataManager()->clear(80,80,490,310,quint8(0)); pixelSelection->convertToQImage(0).save("TEST_FILTER_SELECTION.png"); } void KisFilterSelectionsBenchmark::initFilter(const QString &name) { m_filter = KisFilterRegistry::instance()->value(name); Q_ASSERT(m_filter); m_configuration = m_filter->defaultConfiguration(0); dbgKrita << "Filter initialized:" << name; } void KisFilterSelectionsBenchmark::testFilter(const QString &name) { blockSignals(true); initFilter(name); testUsualSelections(WARMUP_CYCLES); testUsualSelections(NUM_CYCLES); testGoodSelections(WARMUP_CYCLES); testGoodSelections(NUM_CYCLES); testNoSelections(WARMUP_CYCLES); testNoSelections(NUM_CYCLES); testBitBltWOSelections(WARMUP_CYCLES); testBitBltWOSelections(NUM_CYCLES); testBitBltSelections(WARMUP_CYCLES); testBitBltSelections(NUM_CYCLES); blockSignals(false); } void KisFilterSelectionsBenchmark::testAll() { initSelection(); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); QImage image(QString(FILES_DATA_DIR) + QDir::separator() + "hakonepa.png"); m_device = new KisPaintDevice(cs); m_device->convertFromQImage(image, 0, 0, 0); testFilter("brightnesscontrast"); testFilter("invert"); // testFilter("levels"); } void KisFilterSelectionsBenchmark::testUsualSelections(int num) { KisPaintDeviceSP projection = new KisPaintDevice(m_device->colorSpace()); double avTime; KisTimeCounter timer; QRect filterRect = m_selection->selectedExactRect(); timer.restart(); for (int i = 0; i < num; i++) { KisTransaction transac(projection, 0); m_filter->process(m_device, projection, m_selection, filterRect, m_configuration, 0); } avTime = double(timer.elapsed()) / num; projection->convertToQImage(0).save("TFS__USUAL_SELECTIONS.png"); if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "Selections inside filter:\t\t" << avTime; } void KisFilterSelectionsBenchmark::testNoSelections(int num) { KisPaintDeviceSP projection = new KisPaintDevice(m_device->colorSpace()); double avTime; KisTimeCounter timer; QRect filterRect = m_selection->selectedExactRect(); timer.restart(); for (int i = 0; i < num; i++) { KisTransaction transac(projection, 0); m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0); } avTime = double(timer.elapsed()) / num; projection->convertToQImage(0).save("TFS__NO_SELECTIONS.png"); if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "No Selections:\t\t\t\t" << avTime; } void KisFilterSelectionsBenchmark::testGoodSelections(int num) { #if(USE_GOOD_SELECTIONS==1) KisPaintDeviceSP projection = new KisPaintDevice(m_device->colorSpace()); double avTime; KisTimeCounter timer; QRect filterRect = m_selection->selectedExactRect(); KisConstProcessingInformation src(m_device, filterRect.topLeft(), m_selection); KisProcessingInformation dst(projection, filterRect.topLeft(), 0); timer.restart(); for (int i = 0; i < num; i++) { KisTransaction transac(0, projection, 0); m_filter->processSpecial(src, dst, filterRect.size(), m_configuration, 0); } avTime = double(timer.elapsed()) / num; projection->convertToQImage(0).save("TFS__GOOD_SELECTIONS.png"); if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "Selections with alpha (filter):\t" << avTime; #else /* if (USE_GOOD_SELECTIONS!=1) */ if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "Selections with alpha (filter):\t [Disabled]"; #endif } void KisFilterSelectionsBenchmark::testBitBltWOSelections(int num) { KisPaintDeviceSP projection = new KisPaintDevice(m_device->colorSpace()); double avTime; KisTimeCounter timer; QRect filterRect = m_selection->selectedExactRect(); timer.restart(); for (int i = 0; i < num; i++) { KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace()); KisTransaction transac(cacheDevice, 0); m_filter->process(m_device, projection, 0, filterRect, m_configuration, 0); KisPainter painter(projection); painter.beginTransaction(); painter.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN)); painter.bitBlt(filterRect.topLeft(), cacheDevice, filterRect); painter.deleteTransaction(); } avTime = double(timer.elapsed()) / num; projection->convertToQImage(0).save("TFS__BITBLT_WO_SELECTIONS.png"); if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "bitBlt w/o sel:\t\t\t" << avTime; } void KisFilterSelectionsBenchmark::testBitBltSelections(int num) { KisPaintDeviceSP projection = new KisPaintDevice(m_device->colorSpace()); double avTime; KisTimeCounter timer; QRect filterRect = m_selection->selectedExactRect(); timer.restart(); for (int i = 0; i < num; i++) { KisPaintDeviceSP cacheDevice = new KisPaintDevice(projection->colorSpace()); KisTransaction transac(cacheDevice, 0); m_filter->process(m_device, cacheDevice, 0, filterRect, m_configuration, 0); KisPainter gc(projection); gc.beginTransaction(); gc.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_ALPHA_DARKEN)); gc.setSelection(m_selection); gc.bitBlt(filterRect.topLeft(), cacheDevice, filterRect); gc.deleteTransaction(); } avTime = double(timer.elapsed()) / num; projection->convertToQImage(0).save("TFS__BITBLT_WITH_SELECTIONS.png"); if (num > WARMUP_CYCLES || SHOW_WARMUPS) dbgKrita << "bitBlt with sel:\t\t\t" << avTime; } QTEST_MAIN(KisFilterSelectionsBenchmark) diff --git a/krita/benchmarks/kis_floodfill_benchmark.cpp b/krita/benchmarks/kis_floodfill_benchmark.cpp index c38d1e7414..9d38b9fb00 100644 --- a/krita/benchmarks/kis_floodfill_benchmark.cpp +++ b/krita/benchmarks/kis_floodfill_benchmark.cpp @@ -1,114 +1,113 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "kis_benchmark_values.h" -#include "kis_paint_device.h" #include "kis_random_accessor_ng.h" #include #include #include #include #include "kis_floodfill_benchmark.h" #include #include void KisFloodFillBenchmark::initTestCase() { m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_device = new KisPaintDevice(m_colorSpace); m_color = KoColor(m_colorSpace); QColor qcolor(Qt::red); srand(31524744); int tilew = 38; int tileh = 56; m_color.fromQColor(QColor(0,0,0,0)); // default pixel m_device->fill( 0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT,m_color.data() ); // fill the image with red ellipses (like some random dabs) m_color.fromQColor(Qt::red); KisPainter painter(m_device); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setPaintColor(m_color); int x = 0; int y = 0; for (int i = 0; i < 100;i++){ x = rand() % GMP_IMAGE_WIDTH; y = rand() % GMP_IMAGE_HEIGHT; // plus 10 so that we don't fill the ellipse painter.paintEllipse(x+ 10, y+ 10, tilew, tileh); } } void KisFloodFillBenchmark::benchmarkFlood() { KoColor fg(m_colorSpace); KoColor bg(m_colorSpace); fg.fromQColor(Qt::blue); bg.fromQColor(Qt::black); QBENCHMARK { KisFillPainter fillPainter(m_device); //setupPainter(&fillPainter); fillPainter.setPaintColor( fg ); fillPainter.setBackgroundColor( bg ); fillPainter.beginTransaction(kundo2_noi18n("Flood Fill")); //fillPainter.setProgress(updater->startSubtask()); fillPainter.setOpacity(OPACITY_OPAQUE_U8); // default fillPainter.setFillThreshold(15); fillPainter.setCompositeOp(COMPOSITE_OVER); fillPainter.setCareForSelection(true); fillPainter.setWidth(GMP_IMAGE_WIDTH); fillPainter.setHeight(GMP_IMAGE_HEIGHT); // fill twice fillPainter.fillColor(1, 1, m_device); fillPainter.deleteTransaction(); } // uncomment this to see the output //QImage out = m_device->convertToQImage(m_colorSpace->profile(),0,0,GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT); //out.save("fill_output.png"); } void KisFloodFillBenchmark::cleanupTestCase() { } QTEST_MAIN(KisFloodFillBenchmark) diff --git a/krita/benchmarks/kis_floodfill_benchmark.h b/krita/benchmarks/kis_floodfill_benchmark.h index c152f9ebc3..522a41a38e 100644 --- a/krita/benchmarks/kis_floodfill_benchmark.h +++ b/krita/benchmarks/kis_floodfill_benchmark.h @@ -1,52 +1,50 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_FLOODFILL_BENCHMARK_H #define KIS_FLOODFILL_BENCHMARK_H #include #include #include #include -class KisPaintDevice; -class KoColorSpace; class KoColor; class KisFloodFillBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KoColor m_color; KisPaintDeviceSP m_device; int m_startX; int m_startY; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkFlood(); }; #endif diff --git a/krita/benchmarks/kis_gradient_benchmark.cpp b/krita/benchmarks/kis_gradient_benchmark.cpp index 2e095f4721..ae4eed189e 100644 --- a/krita/benchmarks/kis_gradient_benchmark.cpp +++ b/krita/benchmarks/kis_gradient_benchmark.cpp @@ -1,92 +1,91 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2010 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "kis_benchmark_values.h" -#include "kis_paint_device.h" #include "kis_random_accessor_ng.h" #include #include #include #include #include "kis_gradient_benchmark.h" #include #include #include void KisGradientBenchmark::initTestCase() { m_colorSpace = KoColorSpaceRegistry::instance()->rgb8(); m_device = new KisPaintDevice(m_colorSpace); m_color = KoColor(m_colorSpace); m_color.fromQColor(QColor(0,0,0,0)); // default pixel m_device->fill( 0,0,GMP_IMAGE_WIDTH, GMP_IMAGE_HEIGHT,m_color.data() ); } void KisGradientBenchmark::benchmarkGradient() { KoColor fg(m_colorSpace); KoColor bg(m_colorSpace); fg.fromQColor(Qt::blue); bg.fromQColor(Qt::black); QBENCHMARK { QLinearGradient grad; grad.setColorAt(0, Qt::white); grad.setColorAt(1.0, Qt::red); KoAbstractGradient* kograd = KoStopGradient::fromQGradient(&grad); Q_ASSERT(kograd); KisGradientPainter fillPainter(m_device); //setupPainter(&fillPainter); fillPainter.setGradient(kograd); fillPainter.beginTransaction(kundo2_noi18n("Gradient Fill")); //fillPainter.setProgress(updater->startSubtask()); fillPainter.setOpacity(OPACITY_OPAQUE_U8); // default fillPainter.setCompositeOp(COMPOSITE_OVER); fillPainter.setGradientShape(KisGradientPainter::GradientShapeBiLinear); fillPainter.paintGradient(QPointF(0,0), QPointF(3000,3000), KisGradientPainter::GradientRepeatNone, true, false, 0, 0, GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT); fillPainter.deleteTransaction(); } // uncomment this to see the output QImage out = m_device->convertToQImage(m_colorSpace->profile(),0,0,GMP_IMAGE_WIDTH,GMP_IMAGE_HEIGHT); out.save("fill_output.png"); } void KisGradientBenchmark::cleanupTestCase() { } QTEST_MAIN(KisGradientBenchmark) diff --git a/krita/benchmarks/kis_gradient_benchmark.h b/krita/benchmarks/kis_gradient_benchmark.h index ed35e0aa2e..e19ee0db37 100644 --- a/krita/benchmarks/kis_gradient_benchmark.h +++ b/krita/benchmarks/kis_gradient_benchmark.h @@ -1,52 +1,50 @@ /* * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GRADIENT_BENCHMARK_H #define KIS_GRADIENT_BENCHMARK_H #include #include #include #include -class KisPaintDevice; -class KoColorSpace; class KoColor; class KisGradientBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KoColor m_color; KisPaintDeviceSP m_device; int m_startX; int m_startY; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkGradient(); }; #endif diff --git a/krita/benchmarks/kis_hline_iterator_benchmark.h b/krita/benchmarks/kis_hline_iterator_benchmark.h index 92c305d3fa..e835842969 100644 --- a/krita/benchmarks/kis_hline_iterator_benchmark.h +++ b/krita/benchmarks/kis_hline_iterator_benchmark.h @@ -1,65 +1,65 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_HLINEITERATOR_BENCHMARK_H #define KIS_HLINEITERATOR_BENCHMARK_H #include class KisPaintDevice; -class KoColorSpace; class KoColor; +class KoColorSpace; class KisHLineIteratorBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KisPaintDevice * m_device; KoColor * m_color; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkCreation(); // memcpy from KoColor to device void benchmarkWriteBytes(); // memcpy from device to KoColor void benchmarkReadBytes(); // const hline iterator used void benchmarkConstReadBytes(); // copy from one device to another void benchmarkReadWriteBytes(); void benchmarkReadWriteBytes2(); void benchmarkNoMemCpy(); void benchmarkConstNoMemCpy(); // copy from one device to another void benchmarkTwoIteratorsNoMemCpy(); }; #endif diff --git a/krita/benchmarks/kis_level_filter_benchmark.h b/krita/benchmarks/kis_level_filter_benchmark.h index e8cf75ec09..bfbb740b6d 100644 --- a/krita/benchmarks/kis_level_filter_benchmark.h +++ b/krita/benchmarks/kis_level_filter_benchmark.h @@ -1,47 +1,45 @@ /* * Copyright (c) 2015 Thorsten Zachmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_LEVEL_FILTER_BENCHMARK_H #define KIS_LEVEL_FILTER_BENCHMARK_H #include #include #include #include -class KisPaintDevice; -class KoColorSpace; class KoColor; class KisLevelFilterBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KisPaintDeviceSP m_device; KoColor m_color; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkFilter(); }; #endif // KIS_LEVEL_FILTER_BENCHMARK_H diff --git a/krita/benchmarks/kis_painter_benchmark.h b/krita/benchmarks/kis_painter_benchmark.h index 5131e8813a..155a1d7d07 100644 --- a/krita/benchmarks/kis_painter_benchmark.h +++ b/krita/benchmarks/kis_painter_benchmark.h @@ -1,55 +1,54 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTER_BENCHMARK_H #define KIS_PAINTER_BENCHMARK_H #include #include #include -class KoColorSpace; class KisPainterBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KoColor m_color; QVector m_points; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkBitBlt(); void benchmarkFastBitBlt(); void benchmarkBitBltSelection(); void benchmarkFixedBitBlt(); void benchmarkFixedBitBltSelection(); void benchmarkDrawThickLine(); void benchmarkDrawQtLine(); void benchmarkDrawScanLine(); }; #endif diff --git a/krita/benchmarks/kis_random_iterator_benchmark.h b/krita/benchmarks/kis_random_iterator_benchmark.h index 321b4368e7..0a1c0cf3ad 100644 --- a/krita/benchmarks/kis_random_iterator_benchmark.h +++ b/krita/benchmarks/kis_random_iterator_benchmark.h @@ -1,63 +1,63 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_RANDOM_ITERATOR_BENCHMARK_H #define KIS_RANDOM_ITERATOR_BENCHMARK_H #include class KisPaintDevice; -class KoColorSpace; class KoColor; +class KoColorSpace; class KisRandomIteratorBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KisPaintDevice * m_device; KoColor * m_color; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkCreation(); // memcpy from KoColor to device void benchmarkWriteBytes(); // memcpy from device to KoColor void benchmarkReadBytes(); // const vline iterator used void benchmarkConstReadBytes(); // copy from one device to another void benchmarkReadWriteBytes(); // randomly copy data void benchmarkTotalRandom(); // randomly copy data void benchmarkTotalRandomConst(); // tile by tile benchmark void benchmarkTileByTileWrite(); void benchmarkNoMemCpy(); void benchmarkConstNoMemCpy(); void benchmarkTwoIteratorsNoMemCpy(); }; #endif diff --git a/krita/benchmarks/kis_stroke_benchmark.h b/krita/benchmarks/kis_stroke_benchmark.h index 8467d6ddd3..582246f08c 100644 --- a/krita/benchmarks/kis_stroke_benchmark.h +++ b/krita/benchmarks/kis_stroke_benchmark.h @@ -1,132 +1,131 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_STROKE_BENCHMARK_H #define KIS_STROKE_BENCHMARK_H #include #include #include #include #include #include #include -class KoColorSpace; const QString PRESET_FILE_NAME = "hairy-benchmark1.kpp"; class KisStrokeBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KoColor m_color; KisImageSP m_image; KisLayerSP m_layer; KisPainter * m_painter; KisPaintInformation m_pi1; KisPaintInformation m_pi2; KisPaintInformation m_pi3; QPointF m_c1; QPointF m_c2; QVector m_startPoints; QVector m_endPoints; void initCurvePoints(int width, int height); void initLines(int width, int height); QString m_dataPath; QString m_outputPath; private: inline void benchmarkRandomLines(QString presetFileName); inline void benchmarkStroke(QString presetFileName); inline void benchmarkLine(QString presetFileName); inline void benchmarkCircle(QString presetFileName); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void init(); // Autobrush void pixelbrush300px(); void pixelbrush300pxRL(); // Soft brush benchmarks void softbrushDefault30(); void softbrushDefault30RL(); void softbrushCircle30(); void softbrushFullFeatures30(); void softbrushFullFeatures30RL(); void softbrushSoftness(); void softbrushOpacity(); // Hairy brush benchmarks void hairy30pxDefault(); void hairy30pxDefaultRL(); void hairy30pxAntiAlias(); void hairy30pxAntiAliasRL(); void hairy30px30density(); void hairy30px30densityRL(); void hairy30InkDepletion(); void hairy30InkDepletionRL(); // Spray brush benchmark1 void spray30px21particles(); void spray30px21particlesRL(); void sprayPencil(); void sprayPencilRL(); void sprayPixels(); void sprayPixelsRL(); void sprayTexture(); void sprayTextureRL(); void dynabrush(); void dynabrushRL(); void deformBrush(); void deformBrushRL(); void experimental(); void experimentalCircle(); void colorsmudge(); void colorsmudgeRL(); /* void predefinedBrush(); void predefinedBrushRL(); */ void benchmarkRand(); void benchmarkRand48(); }; #endif diff --git a/krita/benchmarks/kis_vline_iterator_benchmark.h b/krita/benchmarks/kis_vline_iterator_benchmark.h index 918b1f19d9..1b3d25fec1 100644 --- a/krita/benchmarks/kis_vline_iterator_benchmark.h +++ b/krita/benchmarks/kis_vline_iterator_benchmark.h @@ -1,57 +1,57 @@ /* * Copyright (c) 2010 Lukáš Tvrdý lukast.dev@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_VLINE_ITERATOR_BENCHMARK_H #define KIS_VLINE_ITERATOR_BENCHMARK_H #include class KisPaintDevice; -class KoColorSpace; class KoColor; +class KoColorSpace; class KisVLineIteratorBenchmark : public QObject { Q_OBJECT private: const KoColorSpace * m_colorSpace; KisPaintDevice * m_device; KoColor * m_color; private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void benchmarkCreation(); // memcpy from KoColor to device void benchmarkWriteBytes(); // memcpy from device to KoColor void benchmarkReadBytes(); // const vline iterator used void benchmarkConstReadBytes(); // copy from one device to another void benchmarkReadWriteBytes(); void benchmarkNoMemCpy(); void benchmarkConstNoMemCpy(); void benchmarkTwoIteratorsNoMemCpy(); }; #endif diff --git a/krita/libbrush/abr_struct_parser.cpp b/krita/libbrush/abr_struct_parser.cpp index baf7a8e094..50ff220802 100644 --- a/krita/libbrush/abr_struct_parser.cpp +++ b/krita/libbrush/abr_struct_parser.cpp @@ -1,306 +1,305 @@ /* * Copyright (c) 2010 Valek Filippov * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "abr_struct_parser.h" -#include "kis_abr_translator.h" AbrStructParser::AbrStructParser() { m_types.insert(PATT, P_PATT); m_types.insert(DESC, P_DESC); m_types.insert(VLLS, P_VLLS); m_types.insert(TEXT, P_TEXT); m_types.insert(OBJC, P_OBJC); m_types.insert(UNTF, P_UNTF); m_types.insert(BOOL, P_BOOL); m_types.insert(LONG, P_LONG); m_types.insert(DOUB, P_DOUB); m_types.insert(ENUM, P_ENUM); m_types.insert(TDTA, P_TDTA); } AbrStructParser::~AbrStructParser() { } QString AbrStructParser::p_patt(QDataStream &buf) { // didn't rev.engineered yet Q_UNUSED(buf); return QString(""); } QString AbrStructParser::p_tdta(QDataStream &buf) { quint32 size; buf >> size; ushort * text = new ushort[size]; for (quint32 i = 0; i < size; i++) { buf >> text[i]; } return "(tdta:" + QString::number(size) + ')' + QString::fromUtf16(text, size); } QString AbrStructParser::p_desc(QDataStream &buf) { // convert 4 bytes as big-endian unsigned long quint32 size; // 22 + 4 buf >> size; buf.skipRawData(22); return QString::number(size); } QString AbrStructParser::p_long(QDataStream &buf) { quint32 size; buf >> size; return QString::number(size); } QString AbrStructParser::p_vlls(QDataStream &buf) { quint32 size; buf >> size; return QString::number(size); } QString AbrStructParser::p_text(QDataStream &buf) { quint32 size; buf >> size; ushort * text = new ushort[size + 1]; for (quint32 i = 0; i < size; i++) { buf >> text[i]; } text[size] = '\0'; return QString::fromUtf16(text); } QString AbrStructParser::p_objc(QDataStream &buf) { // here we lost some data definitly // objnamelen is always 1 and objname is empty string quint32 objnamelen; buf >> objnamelen; char * objname = new char[objnamelen * 2 + 1]; buf.readRawData(objname, objnamelen * 2); objname[ objnamelen * 2 ] = '\0'; Q_ASSERT(objnamelen == 1); quint32 objtypelen; buf >> objtypelen; if (objtypelen == 0) { objtypelen = 4; } char * typeName = new char[objtypelen + 1]; buf.readRawData(typeName, objtypelen); typeName [objtypelen] = '\0'; quint32 value; buf >> value; //return QString::fromLatin1( objname ) + ' ' + QString::fromLatin1(typeName) + ' ' + QString::number(value); return QString::fromLatin1(typeName) + ' ' + QString::number(value); } QString AbrStructParser::p_untf(QDataStream &buf) { char * type = new char[5]; buf.readRawData(type, 4); type[4] = '\0'; double value; buf >> value; return QString::fromLatin1(type) + ' ' + QString::number(value); } QString AbrStructParser::p_bool(QDataStream &buf) { //# ord converts 1 byte number char byte; buf.device()->getChar(&byte); if (byte) return QString("1"); else return QString("0"); } QString AbrStructParser::p_doub(QDataStream &buf) { // unpack 8 bytes ieee 754 value to floating point number double value; buf >> value; return QString::number(value); } QString AbrStructParser::p_enum(QDataStream &buf) { quint32 size1, size2; buf >> size1; if (size1 == 0) { size1 = 4; } char * name1 = new char[size1 + 1]; buf.readRawData(name1, size1); name1[size1] = '\0'; buf >> size2 ; if (size2 == 0) { size2 = 4; } char * name2 = new char[size2 + 1]; buf.readRawData(name2, size2); name2[size2] = '\0'; return QString::fromLatin1(name1) + ' ' + QString::fromLatin1(name2); } quint32 AbrStructParser::parseEntry(QDataStream &buf) { quint32 nlen; buf >> nlen; if (nlen == 0) { nlen = 4; } QString value; if (nlen == MAGIC_OBJC_LENGTH) { value = p_objc(buf); dbgKrita << ABR_PRESET_START << ABR_OBJECT << value; // start to create the preset here m_translator.addEntry(ABR_PRESET_START, ABR_OBJECT, value); } else { // read char with nlen bytes and convert to String char * name = new char[ nlen + 1 ]; int status = buf.readRawData(name, nlen); if (status == -1) { dbgKrita << "Error, name can't be readed"; } name[nlen] = '\0'; char * type = new char[5]; status = buf.readRawData(type, 4); type[4] = '\0'; QString key = QString::fromLatin1(type); if (m_types.contains(key)) { enumFuncNames enumName = m_types[key]; switch (enumName) { case P_PATT: value = p_patt(buf); break; case P_DESC: value = p_desc(buf); break; case P_VLLS: value = p_vlls(buf); break; case P_TEXT: value = p_text(buf); break; case P_OBJC: value = p_objc(buf); break; case P_UNTF: value = p_untf(buf); break; case P_BOOL: value = p_bool(buf); break; case P_LONG: value = p_long(buf); break; case P_DOUB: value = p_doub(buf); break; case P_ENUM: value = p_enum(buf); break; case P_TDTA: value = p_tdta(buf); break; default: dbgKrita << "Freak error occurred!"; break; } QString attributeName = QString::fromLatin1(name); //dbgKrita << attributeName << key << value; m_translator.addEntry(attributeName, key, value); // airbrush is the last parsed attribute of the preset if (attributeName == ABR_AIRBRUSH) { m_translator.finishPreset(); dbgKrita << m_translator.toString(); } } else { dbgKrita << "Unknown key:\t" << name << type; //dbgKrita << p_unkn(buf); return -1; } } return 0; } void AbrStructParser::parse(QString fileName) { QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { dbgKrita << "Can't open file " << fileName; return; } QDataStream buf(&file); // offset in bytes short int vermaj, vermin; buf >> vermaj; buf >> vermin; dbgKrita << "Version: " << vermaj << "." << vermin; int index = file.readAll().indexOf("8BIMdesc"); buf.device()->seek(index); int status = 0; while (!buf.atEnd()) { status = parseEntry(buf); if (status == -1) { // something to break the parsing with fail? dbgKrita << "Finishing with fail..."; break; } } dbgKrita << m_doc.toString(); } int main(int argc, const char * argv[]) { QString fileName; if (argc != 2) { fileName = "test.abr"; } else { fileName = QString::fromLatin1(argv[1]); } AbrStructParser parser; parser.parse(fileName); return 0; } diff --git a/krita/libbrush/kis_abr_brush.cpp b/krita/libbrush/kis_abr_brush.cpp index 4e6c97dea3..4555ac39e1 100644 --- a/krita/libbrush/kis_abr_brush.cpp +++ b/krita/libbrush/kis_abr_brush.cpp @@ -1,100 +1,100 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_abr_brush.h" -#include "kis_brush.h" +#include "kis_abr_brush_collection.h" #include #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" #define DEFAULT_SPACING 0.25 KisAbrBrush::KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent) : KisBrush(filename) , m_parent(parent) { setBrushType(INVALID); setHasColor(false); setSpacing(DEFAULT_SPACING); } bool KisAbrBrush::load() { return true; } bool KisAbrBrush::loadFromDevice(QIODevice */*dev*/) { return true; } bool KisAbrBrush::save() { //Return true, otherwise the brush won't be added to the //resource server if the brush is loaded via import return true; } bool KisAbrBrush::saveToDevice(QIODevice* /*dev*/) const { return true; } void KisAbrBrush::setBrushTipImage(const QImage& image) { setValid(true); setBrushType(MASK); setHasColor(false); KisBrush::setBrushTipImage(image); } void KisAbrBrush::toXML(QDomDocument& d, QDomElement& e) const { e.setAttribute("name", name()); // legacy predefinedBrushToXML("abr_brush", e); KisBrush::toXML(d, e); } QString KisAbrBrush::defaultFileExtension() const { return QString(); } QImage KisAbrBrush::brushTipImage() const { if (KisBrush::brushTipImage().isNull() && m_parent) { m_parent->load(); } return KisBrush::brushTipImage(); } diff --git a/krita/libbrush/kis_abr_brush.h b/krita/libbrush/kis_abr_brush.h index fcb7fe5c6c..6a05b5f80f 100644 --- a/krita/libbrush/kis_abr_brush.h +++ b/krita/libbrush/kis_abr_brush.h @@ -1,74 +1,74 @@ /* * Copyright (c) 2010 Boudewijn Rempt * Copyright (c) 2010 Lukáš Tvrdý * Copyright (c) 2007 Eric Lamarque * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_ABR_BRUSH_ #define KIS_ABR_BRUSH_ #include #include #include "kis_brush.h" #include "kis_types.h" #include "kis_shared.h" #include "kis_paint_information.h" -#include "kis_abr_brush_collection.h" #include "kritabrush_export.h" class KisQImagemask; +class KisAbrBrushCollection; typedef KisSharedPtr KisQImagemaskSP; class QString; class QIODevice; class BRUSH_EXPORT KisAbrBrush : public KisBrush { public: /// Construct brush to load filename later as brush KisAbrBrush(const QString& filename, KisAbrBrushCollection *parent); virtual bool load(); virtual bool loadFromDevice(QIODevice *dev); virtual bool save(); virtual bool saveToDevice(QIODevice* dev) const; /** * @return default file extension for saving the brush */ virtual QString defaultFileExtension() const; virtual QImage brushTipImage() const; friend class KisAbrBrushCollection; virtual void setBrushTipImage(const QImage& image); void toXML(QDomDocument& d, QDomElement& e) const; private: KisAbrBrushCollection *m_parent; }; #endif // KIS_ABR_BRUSH_ diff --git a/krita/libbrush/kis_brush.h b/krita/libbrush/kis_brush.h index 0feb3349ed..91c7baada0 100644 --- a/krita/libbrush/kis_brush.h +++ b/krita/libbrush/kis_brush.h @@ -1,366 +1,365 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_ #define KIS_BRUSH_ #include #include #include #include #include class KisQImagemask; typedef KisSharedPtr KisQImagemaskSP; class QString; -class QPoint; class KoColor; class KoColorSpace; class KisPaintInformation; class KisBoundary; enum enumBrushType { INVALID, MASK, IMAGE, PIPE_MASK, PIPE_IMAGE, AIRBRUSH }; static const qreal DEFAULT_SOFTNESS_FACTOR = 1.0; class KisBrush; typedef KisSharedPtr KisBrushSP; /** * KisBrush is the base class for brush resources. A brush resource * defines one or more images that are used to potato-stamp along * the drawn path. The brush type defines how this brush is used -- * the important difference is between masks (which take the current * painting color) and images (which do not). It is up to the paintop * to make use of this feature. * * Brushes must be serializable to an xml representation and provide * a factory class that can recreate or retrieve the brush based on * this representation. * * XXX: This api is still a big mess -- it needs a good refactoring. * And the whole KoResource architecture is way over-designed. */ class BRUSH_EXPORT KisBrush : public KoResource, public KisShared { public: class ColoringInformation { public: virtual ~ColoringInformation(); virtual const quint8* color() const = 0; virtual void nextColumn() = 0; virtual void nextRow() = 0; }; protected: class PlainColoringInformation : public ColoringInformation { public: PlainColoringInformation(const quint8* color); virtual ~PlainColoringInformation(); virtual const quint8* color() const ; virtual void nextColumn(); virtual void nextRow(); private: const quint8* m_color; }; class PaintDeviceColoringInformation : public ColoringInformation { public: PaintDeviceColoringInformation(const KisPaintDeviceSP source, int width); virtual ~PaintDeviceColoringInformation(); virtual const quint8* color() const ; virtual void nextColumn(); virtual void nextRow(); private: const KisPaintDeviceSP m_source; KisHLineConstIteratorSP m_iterator; }; public: KisBrush(); KisBrush(const QString& filename); virtual ~KisBrush(); virtual bool load() { return false; } virtual bool loadFromDevice(QIODevice *) { return false; } virtual bool save() { return false; } virtual bool saveToDevice(QIODevice* ) const { return false; } /** * @brief brushImage the image the brush tip can paint with. Not all brush types have a single * image. * @return a valid QImage. */ virtual QImage brushTipImage() const; /** * Change the spacing of the brush. * @param spacing a spacing of 1.0 means that strokes will be separated from one time the size * of the brush. */ virtual void setSpacing(double spacing); /** * @return the spacing between two strokes for this brush */ double spacing() const; void setAutoSpacing(bool active, qreal coeff); bool autoSpacingActive() const; qreal autoSpacingCoeff() const; /** * @return the width (for scale == 1.0) */ qint32 width() const; /** * @return the height (for scale == 1.0) */ qint32 height() const; /** * @return the width of the mask for the given scale and angle */ virtual qint32 maskWidth(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the height of the mask for the given scale and angle */ virtual qint32 maskHeight(double scale, double angle, qreal subPixelX, qreal subPixelY, const KisPaintInformation& info) const; /** * @return the logical size of the brush, that is the size measured * in floating point value. * * This value should not be used for calculating future dab sizes * because it doesn't take any rounding into account. The only use * of this metric is calculation of brush-size derivatives like * hotspots and spacing. */ QSizeF characteristicSize(double scaleX, double scaleY, double rotation) const; /** * @return the angle of the mask adding the given angle */ double maskAngle(double angle = 0) const; /** * @return the index of the brush * if the brush consists of multiple images */ virtual quint32 brushIndex(const KisPaintInformation& info) const; /** * The brush type defines how the brush is used. */ virtual enumBrushType brushType() const; QPointF hotSpot(double scaleX, double scaleY, double rotation, const KisPaintInformation& info) const; /** * Returns true if this brush can return something useful for the info. This is used * by Pipe Brushes that can't paint sometimes **/ virtual bool canPaintFor(const KisPaintInformation& /*info*/); /** * Is called by the cache, when cache hit has happened. * Having got this notification the brush can update the counters * of dabs, generate some new random values if needed. * * Currently, this is used by pipe'd brushes to implement * incremental and random parasites */ void notifyCachedDabPainted(); /** * Return a fixed paint device that contains a correctly scaled image dab. */ virtual KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0) const; /** * Apply the brush mask to the pixels in dst. Dst should be big enough! */ void mask(KisFixedPaintDeviceSP dst, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * clear dst fill it with a mask colored with KoColor */ void mask(KisFixedPaintDeviceSP dst, const KoColor& color, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * clear dst and fill it with a mask colored with the corresponding colors of src */ void mask(KisFixedPaintDeviceSP dst, const KisPaintDeviceSP src, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; virtual bool hasColor() const; /** * Create a mask and either mask dst (that is, change all alpha values of the * existing pixels to those of the mask) or, if coloringInfo is present, clear * dst and fill dst with pixels according to coloringInfo, masked according to the * generated mask. * * @param dst the destination that will be draw on the image, and this function * will edit its alpha channel * @param coloringInfo coloring information that will be copied on the dab, it can be null * @param scale a scale applied on the alpha mask * @param angle a rotation applied on the alpha mask * @param info the painting information (this is only and should only be used by * KisImagePipeBrush and only to be backward compatible with the Gimp, * KisImagePipeBrush is ignoring scale and angle information) * @param subPixelX sub position of the brush (contained between 0.0 and 1.0) * @param subPixelY sub position of the brush (contained between 0.0 and 1.0) * * @return a mask computed from the grey-level values of the * pixels in the brush. */ virtual void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, ColoringInformation* coloringInfo, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; /** * Serialize this brush to XML. */ virtual void toXML(QDomDocument& , QDomElement&) const; static KisBrushSP fromXML(const QDomElement& element); virtual const KisBoundary* boundary() const; virtual QPainterPath outline() const; virtual void setScale(qreal _scale); qreal scale() const; virtual void setAngle(qreal _angle); qreal angle() const; void prepareBrushPyramid() const; void clearBrushPyramid(); //protected: KisBrush(const KisBrush& rhs); void setWidth(qint32 width); void setHeight(qint32 height); void setHotSpot(QPointF); /** * The image is used to represent the brush in the gui, and may also, depending on the brush type * be used to define the actual brush instance. */ virtual void setBrushTipImage(const QImage& image); /** * XXX */ virtual void setBrushType(enumBrushType type); friend class KisBrushTest; virtual void setHasColor(bool hasColor); /** * Returns true if the brush has a bunch of pixels almost * fully transparent in the very center. If the brush is pierced, * then dulling mode may not work correctly due to empty samples. * * WARNING: this method is relatively expensive since it iterates * up to 100 pixels of the brush. */ bool isPiercedApprox() const; protected: void resetBoundary(); void predefinedBrushToXML(const QString &type, QDomElement& e) const; private: friend class KisImagePipeBrushTest; // Initialize our boundary void generateBoundary() const; struct Private; Private* const d; }; #endif // KIS_BRUSH_ diff --git a/krita/libbrush/kis_brush_registry.cpp b/krita/libbrush/kis_brush_registry.cpp index 664bbfb371..3f3543f840 100644 --- a/krita/libbrush/kis_brush_registry.cpp +++ b/krita/libbrush/kis_brush_registry.cpp @@ -1,78 +1,77 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_registry.h" #include #include #include #include #include #include "kis_brush_server.h" -#include "kis_brush_factory.h" #include "kis_auto_brush_factory.h" #include "kis_text_brush_factory.h" #include "kis_predefined_brush_factory.h" Q_GLOBAL_STATIC(KisBrushRegistry, s_instance) KisBrushRegistry::KisBrushRegistry() { KisBrushServer::instance(); } KisBrushRegistry::~KisBrushRegistry() { foreach(const QString & id, keys()) { delete get(id); } dbgRegistry << "deleting KisBrushRegistry"; } KisBrushRegistry* KisBrushRegistry::instance() { if (!s_instance.exists()) { s_instance->add(new KisAutoBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("gbr_brush")); s_instance->add(new KisPredefinedBrushFactory("abr_brush")); s_instance->add(new KisTextBrushFactory()); s_instance->add(new KisPredefinedBrushFactory("png_brush")); s_instance->add(new KisPredefinedBrushFactory("svg_brush")); KoPluginLoader::instance()->load("Krita/Brush", "Type == 'Service' and ([X-Krita-Version] == 28)"); } return s_instance; } KisBrushSP KisBrushRegistry::getOrCreateBrush(const QDomElement& element) { QString brushType = element.attribute("type"); if (brushType.isEmpty()) return 0; KisBrushFactory* factory = get(brushType); if (!factory) return 0; KisBrushSP brush = factory->getOrCreateBrush(element); return brush; } diff --git a/krita/libbrush/kis_brush_server.h b/krita/libbrush/kis_brush_server.h index 9074147f87..25663bf384 100644 --- a/krita/libbrush/kis_brush_server.h +++ b/krita/libbrush/kis_brush_server.h @@ -1,67 +1,66 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_SERVER_H #define KIS_BRUSH_SERVER_H #include #include #include #include #include #include "kritabrush_export.h" #include "kis_brush.h" -class KoResource; class KoResourceLoaderThread; typedef KoResourceServer > KisBrushResourceServer; typedef KoResourceServerAdapter > KisBrushResourceServerAdapter; /** * */ class BRUSH_EXPORT KisBrushServer : public QObject { Q_OBJECT public: KisBrushServer(); virtual ~KisBrushServer(); KisBrushResourceServer* brushServer(bool block = true); static KisBrushServer* instance(); public Q_SLOTS: void slotRemoveBlacklistedResources(); private: KisBrushServer(const KisBrushServer&); KisBrushServer operator=(const KisBrushServer&); KisBrushResourceServer* m_brushServer; private: KoResourceLoaderThread *m_brushThread; }; #endif diff --git a/krita/libbrush/kis_gbr_brush.cpp b/krita/libbrush/kis_gbr_brush.cpp index cc29f36e18..296b95bdd9 100644 --- a/krita/libbrush/kis_gbr_brush.cpp +++ b/krita/libbrush/kis_gbr_brush.cpp @@ -1,524 +1,522 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Adrian Page * Copyright (c) 2005 Bart Coppens * Copyright (c) 2007 Cyrille Berger * Copyright (c) 2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include // htonl #include "kis_gbr_brush.h" -#include "kis_brush.h" - #include #include #include #include #include #include #include #include #include "kis_datamanager.h" #include "kis_paint_device.h" #include "kis_global.h" #include "kis_image.h" struct GimpBrushV1Header { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ }; /// All fields are in MSB on disk! struct GimpBrushHeader { quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 width; /* width of brush */ quint32 height; /* height of brush */ quint32 bytes; /* depth of brush in bytes */ /* The following are only defined in version 2 */ quint32 magic_number; /* GIMP brush magic number */ quint32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ }; // Needed, or the GIMP won't open it! quint32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); struct KisGbrBrush::Private { QByteArray data; bool ownData; /* seems to indicate that @ref data is owned by the brush, but in Qt4.x this is already guaranteed... so in reality it seems more to indicate whether the data is loaded from file (ownData = true) or memory (ownData = false) */ bool useColorAsMask; quint32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ quint32 version; /* brush file version # */ quint32 bytes; /* depth of brush in bytes */ quint32 magic_number; /* GIMP brush magic number */ }; #define DEFAULT_SPACING 0.25 KisGbrBrush::KisGbrBrush(const QString& filename) : KisBrush(filename) , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); } KisGbrBrush::KisGbrBrush(const QString& filename, const QByteArray& data, qint32 & dataPos) : KisBrush(filename) , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); d->data = QByteArray::fromRawData(data.data() + dataPos, data.size() - dataPos); init(); d->data.clear(); dataPos += d->header_size + (width() * height() * d->bytes); } KisGbrBrush::KisGbrBrush(KisPaintDeviceSP image, int x, int y, int w, int h) : KisBrush() , d(new Private) { d->ownData = true; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); initFromPaintDev(image, x, y, w, h); } KisGbrBrush::KisGbrBrush(const QImage& image, const QString& name) : KisBrush() , d(new Private) { d->ownData = false; d->useColorAsMask = false; setHasColor(false); setSpacing(DEFAULT_SPACING); setBrushTipImage(image); setName(name); } KisGbrBrush::KisGbrBrush(const KisGbrBrush& rhs) : KisBrush(rhs) , d(new Private(*rhs.d)) { setName(rhs.name()); d->data = QByteArray(); setValid(rhs.valid()); } KisGbrBrush::~KisGbrBrush() { delete d; } bool KisGbrBrush::load() { QFile file(filename()); if (file.size() == 0) return false; file.open(QIODevice::ReadOnly); bool res = loadFromDevice(&file); file.close(); return res; } bool KisGbrBrush::loadFromDevice(QIODevice *dev) { if (d->ownData) { d->data = dev->readAll(); } return init(); } bool KisGbrBrush::init() { GimpBrushHeader bh; if (sizeof(GimpBrushHeader) > (uint)d->data.size()) { return false; } memcpy(&bh, d->data, sizeof(GimpBrushHeader)); bh.header_size = ntohl(bh.header_size); d->header_size = bh.header_size; bh.version = ntohl(bh.version); d->version = bh.version; bh.width = ntohl(bh.width); bh.height = ntohl(bh.height); bh.bytes = ntohl(bh.bytes); d->bytes = bh.bytes; bh.magic_number = ntohl(bh.magic_number); d->magic_number = bh.magic_number; if (bh.version == 1) { // No spacing in version 1 files so use Gimp default bh.spacing = static_cast(DEFAULT_SPACING * 100); } else { bh.spacing = ntohl(bh.spacing); if (bh.spacing > 1000) { return false; } } setSpacing(bh.spacing / 100.0); if (bh.header_size > (uint)d->data.size() || bh.header_size == 0) { return false; } QString name; if (bh.version == 1) { // Version 1 has no magic number or spacing, so the name // is at a different offset. Character encoding is undefined. const char *text = d->data.constData() + sizeof(GimpBrushV1Header); name = QString::fromLatin1(text, bh.header_size - sizeof(GimpBrushV1Header) - 1); } else { // ### Version = 3->cinepaint; may be float16 data! // Version >=2: UTF-8 encoding is used name = QString::fromUtf8(d->data.constData() + sizeof(GimpBrushHeader), bh.header_size - sizeof(GimpBrushHeader) - 1); } setName(name); if (bh.width == 0 || bh.height == 0) { return false; } QImage::Format imageFormat; if (bh.bytes == 1) { imageFormat = QImage::Format_Indexed8; } else { imageFormat = QImage::Format_ARGB32; } QImage image(QImage(bh.width, bh.height, imageFormat)); if (image.isNull()) { return false; } qint32 k = bh.header_size; if (bh.bytes == 1) { QVector table; for (int i = 0; i < 256; ++i) table.append(qRgb(i, i, i)); image.setColorTable(table); // Grayscale if (static_cast(k + bh.width * bh.height) > d->data.size()) { return false; } setHasColor(false); for (quint32 y = 0; y < bh.height; y++) { uchar *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k++) { qint32 val = 255 - static_cast(d->data[k]); *pixel = val; ++pixel; } } } else if (bh.bytes == 4) { // RGBA if (static_cast(k + (bh.width * bh.height * 4)) > d->data.size()) { return false; } setHasColor(true); for (quint32 y = 0; y < bh.height; y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (quint32 x = 0; x < bh.width; x++, k += 4) { *pixel = qRgba(d->data[k], d->data[k + 1], d->data[k + 2], d->data[k + 3]); ++pixel; } } } else { warnKrita << "WARNING: loading of GBR brushes with" << bh.bytes << "bytes per pixel is not supported"; return false; } setWidth(image.width()); setHeight(image.height()); if (d->ownData) { d->data.resize(0); // Save some memory, we're using enough of it as it is. } setValid(image.width() != 0 && image.height() != 0); setBrushTipImage(image); return true; } bool KisGbrBrush::initFromPaintDev(KisPaintDeviceSP image, int x, int y, int w, int h) { // Forcefully convert to RGBA8 // XXX profile and exposure? setBrushTipImage(image->convertToQImage(0, x, y, w, h, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags())); setName(image->objectName()); setHasColor(true); return true; } bool KisGbrBrush::save() { QFile file(filename()); file.open(QIODevice::WriteOnly | QIODevice::Truncate); bool ok = saveToDevice(&file); file.close(); return ok; } bool KisGbrBrush::saveToDevice(QIODevice* dev) const { GimpBrushHeader bh; QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8 char const* name = utf8Name.data(); int nameLength = qstrlen(name); int wrote; bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength + 1); bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff bh.width = htonl(width()); bh.height = htonl(height()); // Hardcoded, 4 bytes RGBA or 1 byte GREY if (!hasColor()) { bh.bytes = htonl(1); } else { bh.bytes = htonl(4); } bh.magic_number = htonl(GimpV2BrushMagic); bh.spacing = htonl(static_cast(spacing() * 100.0)); // Write header: first bh, then the name QByteArray bytes = QByteArray::fromRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); wrote = dev->write(bytes); bytes.clear(); if (wrote == -1) { return false; } wrote = dev->write(name, nameLength + 1); if (wrote == -1) { return false; } int k = 0; QImage image = brushTipImage(); if (!hasColor()) { bytes.resize(width() * height()); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { QRgb c = image.pixel(x, y); bytes[k++] = static_cast(255 - qRed(c)); // red == blue == green } } } else { bytes.resize(width() * height() * 4); for (qint32 y = 0; y < height(); y++) { for (qint32 x = 0; x < width(); x++) { // order for gimp brushes, v2 is: RGBA QRgb pixel = image.pixel(x, y); bytes[k++] = static_cast(qRed(pixel)); bytes[k++] = static_cast(qGreen(pixel)); bytes[k++] = static_cast(qBlue(pixel)); bytes[k++] = static_cast(qAlpha(pixel)); } } } wrote = dev->write(bytes); if (wrote == -1) { return false; } KoResource::saveToDevice(dev); return true; } QImage KisGbrBrush::brushTipImage() const { QImage image = KisBrush::brushTipImage(); if (hasColor() && useColorAsMask()) { for (int y = 0; y < image.height(); y++) { QRgb *pixel = reinterpret_cast(image.scanLine(y)); for (int x = 0; x < image.width(); x++) { QRgb c = pixel[x]; int a = qGray(c); pixel[x] = qRgba(a, a, a, qAlpha(c)); } } } return image; } enumBrushType KisGbrBrush::brushType() const { return !hasColor() || useColorAsMask() ? MASK : IMAGE; } void KisGbrBrush::setBrushType(enumBrushType type) { Q_UNUSED(type); qFatal("FATAL: protected member setBrushType has no meaning for KisGbrBrush"); } void KisGbrBrush::setBrushTipImage(const QImage& image) { KisBrush::setBrushTipImage(image); setValid(true); } /*QImage KisGbrBrush::outline(double pressure) { KisLayerSP layer = image(KoColorSpaceRegistry::instance()->colorSpace("RGBA",0), KisPaintInformation(pressure)); KisBoundary bounds(layer.data()); int w = maskWidth(pressure); int h = maskHeight(pressure); bounds.generateBoundary(w, h); QPixmap pix(bounds.pixmap(w, h)); QImage result; result = pix; return result; }*/ void KisGbrBrush::makeMaskImage() { if (!hasColor()) { return; } QImage brushTip = brushTipImage(); if (brushTip.width() == width() && brushTip.height() == height()) { int imageWidth = width(); int imageHeight = height(); QImage image(imageWidth, imageHeight, QImage::Format_Indexed8); QVector table; for (int i = 0; i < 256; ++i) { table.append(qRgb(i, i, i)); } image.setColorTable(table); for (int y = 0; y < imageHeight; y++) { QRgb *pixel = reinterpret_cast(brushTip.scanLine(y)); uchar * dstPixel = image.scanLine(y); for (int x = 0; x < imageWidth; x++) { QRgb c = pixel[x]; float alpha = qAlpha(c) / 255.0f; // linear interpolation with maximum gray value which is transparent in the mask //int a = (qGray(c) * alpha) + ((1.0 - alpha) * 255); // single multiplication version int a = 255 + alpha * (qGray(c) - 255); dstPixel[x] = (uchar)a; } } setBrushTipImage(image); } setHasColor(false); setUseColorAsMask(false); resetBoundary(); clearBrushPyramid(); } KisGbrBrush* KisGbrBrush::clone() const { return new KisGbrBrush(*this); } void KisGbrBrush::toXML(QDomDocument& d, QDomElement& e) const { predefinedBrushToXML("gbr_brush", e); e.setAttribute("ColorAsMask", QString::number((int)useColorAsMask())); KisBrush::toXML(d, e); } void KisGbrBrush::setUseColorAsMask(bool useColorAsMask) { /** * WARNING: There is a problem in the brush server, since it * returns not copies of brushes, but direct pointers to them. It * means that the brushes are shared among all the currently * present paintops, which might be a problem for e.g. Multihand * Brush Tool. * * Right now, all the instances of Multihand Brush Tool share the * same brush, so there is no problem in this sharing, unless we * reset the internal state of the brush on our way. */ if (useColorAsMask != d->useColorAsMask) { d->useColorAsMask = useColorAsMask; resetBoundary(); clearBrushPyramid(); } } bool KisGbrBrush::useColorAsMask() const { return d->useColorAsMask; } QString KisGbrBrush::defaultFileExtension() const { return QString(".gbr"); } diff --git a/krita/libbrush/kis_imagepipe_brush_p.h b/krita/libbrush/kis_imagepipe_brush_p.h index 7540877d6d..7f78f20756 100644 --- a/krita/libbrush/kis_imagepipe_brush_p.h +++ b/krita/libbrush/kis_imagepipe_brush_p.h @@ -1,119 +1,118 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_IMAGE_PIPE_BRUSH_P_H #define KIS_IMAGE_PIPE_BRUSH_P_H #include "kis_imagepipe_brush.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_global.h" #include "kis_paint_device.h" -#include "kis_brush.h" #include "kis_layer.h" -#include "kis_boundary.h" + /** * The parasite info that gets loaded from the terribly documented gimp pipe brush parasite. * * We only store data we actually use. * * BC: How it seems the dimension stuff interacts with rank, selectionMode and the actual * selection of a brush to be drawn. So apparently you can have at most 4 'dimensions'. * Each dimension has a number of brushes, the rank. Each dimension has an associated selection * mode and placement mode (which we don't use). The selection mode says us in which way * which of the brushes or brush sets will be selected. In the case of a 1-dimensional pipe * brush it is easy. * * However, when there are more dimensions it is a bit harder. You can according to the gimp * source maximally use 4 dimensions. When you want to select a brush, you first go to the * first dimension. Say it has a rank of 2. The code chooses one of the 2 according to the * selection mode. Say we choose 2. Then the currentBrush will skip over all the brushes * from the first element in dimension 1. Then in dimension we pick again from the choices * we have in dimension 2. We again add the appropriate amount to currentBrush. And so on, * until we have reached dimension dim. Or at least, that is how it looks like, we'll know * for sure when we can test it better with >1 dim brushes and Angular selectionMode. **/ class KisPipeBrushParasite { public: /// Set some default values KisPipeBrushParasite() : ncells(0) , dim(0) , needsMovement(false) { init(); } void init(); void sanitize(); /// Initializes the brushesCount helper void setBrushesCount(); /// Load the parasite from the source string KisPipeBrushParasite(const QString& source); /** * Saves a GIMP-compatible representation of this parasite to the device. Also writes the * number of brushes (== ncells) (no trailing '\n') */ bool saveToDevice(QIODevice* dev) const; enum Placement { DefaultPlacement, ConstantPlacement, RandomPlacement }; static int const MaxDim = 4; //qint32 step; qint32 ncells; qint32 dim; // Apparently only used for editing a pipe brush, which we won't at the moment // qint32 cols, rows; // qint32 cellwidth, cellheight; // Apparently the gimp doesn't use this anymore? Anyway it is a bit weird to // paint at someplace else than where your cursor displays it will... //Placement placement; qint32 rank[MaxDim]; KisParasite::SelectionMode selection[MaxDim]; /// The total count of brushes in each dimension (helper) qint32 brushesCount[MaxDim]; /// The current index in each dimension, so that the selection modes know where to start qint32 index[MaxDim]; /// If true, the brush won't be painted when there is no motion bool needsMovement; }; #endif diff --git a/krita/libbrush/kis_text_brush.h b/krita/libbrush/kis_text_brush.h index 019a6d9c71..66b5698058 100644 --- a/krita/libbrush/kis_text_brush.h +++ b/krita/libbrush/kis_text_brush.h @@ -1,94 +1,93 @@ /* * Copyright (c) 2004 Cyrille Berger * Copyright (c) 2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_TEXT_BRUSH_H_ #define _KIS_TEXT_BRUSH_H_ #include #include "kis_brush.h" #include "kritabrush_export.h" -#include "kis_gbr_brush.h" class KisTextBrushesPipe; class BRUSH_EXPORT KisTextBrush : public KisBrush { public: KisTextBrush(); KisTextBrush(const KisTextBrush &rhs); virtual ~KisTextBrush(); void notifyCachedDabPainted(); void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation* coloringInformation, double scaleX, double scaleY, double angle, const KisPaintInformation& info, double subPixelX = 0, double subPixelY = 0, qreal softnessFactor = DEFAULT_SOFTNESS_FACTOR) const; KisFixedPaintDeviceSP paintDevice(const KoColorSpace * colorSpace, double scale, double angle, const KisPaintInformation& info, double subPixelX, double subPixelY) const; bool load() { return false; } virtual bool loadFromDevice(QIODevice *) { return false; } bool save() { return false; } bool saveToDevice(QIODevice* ) const { return false; } void setText(const QString& txt); QString text(void) const; QFont font(); void setFont(const QFont& font); void setPipeMode(bool pipe); bool pipeMode() const; void updateBrush(); void toXML(QDomDocument& , QDomElement&) const; quint32 brushIndex(const KisPaintInformation& info) const; qint32 maskWidth(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const; qint32 maskHeight(double scale, double angle, double subPixelX, double subPixelY, const KisPaintInformation& info) const; void setAngle(qreal _angle); void setScale(qreal _scale); void setSpacing(double _spacing); KisBrush* clone() const; private: QFont m_font; QString m_text; private: KisTextBrushesPipe *m_brushesPipe; }; #endif diff --git a/krita/libglobal/kis_icon_utils.h b/krita/libglobal/kis_icon_utils.h index a8cd089ded..4629efe425 100644 --- a/krita/libglobal/kis_icon_utils.h +++ b/krita/libglobal/kis_icon_utils.h @@ -1,63 +1,62 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ICON_UTILS_H #define __KIS_ICON_UTILS_H #include "kritaglobal_export.h" -class QIcon; class QAbstractButton; class QComboBox; class QAction; class QObject; #include #include namespace KisIconUtils { /** * Load a themed icon using its base name. Use it in * Krita instead of previous KisIconUtils::loadIcon() */ KRITAGLOBAL_EXPORT QIcon loadIcon(const QString &name); /** * This function updates an icon of \p object depending on its * type. See updateIcon() overrides to see the supported types */ KRITAGLOBAL_EXPORT void updateIconCommon(QObject *object); /** * Update an icon of \p button according to the current theme */ KRITAGLOBAL_EXPORT void updateIcon(QAbstractButton *button); /** * Update an icon of \p comboBox according to the current theme */ KRITAGLOBAL_EXPORT void updateIcon(QComboBox *comboBox); /** * Update an icon of \p action according to the current theme */ KRITAGLOBAL_EXPORT void updateIcon(QAction *action); } #endif /* __KIS_ICON_UTILS_H */ diff --git a/krita/libpsd/asl/kis_asl_writer_utils.cpp b/krita/libpsd/asl/kis_asl_writer_utils.cpp index 1ee0bd9f81..7c4651f8b8 100644 --- a/krita/libpsd/asl/kis_asl_writer_utils.cpp +++ b/krita/libpsd/asl/kis_asl_writer_utils.cpp @@ -1,112 +1,111 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_writer_utils.h" #include #include "KoPattern.h" -#include "kis_debug.h" namespace KisAslWriterUtils { void writeRect(const QRect &rect, QIODevice *device) { { const quint32 rectY0 = rect.y(); SAFE_WRITE_EX(device, rectY0); } { const quint32 rectX0 = rect.x(); SAFE_WRITE_EX(device, rectX0); } { const quint32 rectY1 = rect.y() + rect.height(); SAFE_WRITE_EX(device, rectY1); } { const quint32 rectX1 = rect.x() + rect.width(); SAFE_WRITE_EX(device, rectX1); } } void writeUnicodeString(const QString &value, QIODevice *device) { quint32 len = value.length() + 1; SAFE_WRITE_EX(device, len); const quint16 *ptr = value.utf16(); for (quint32 i = 0; i < len; i++) { SAFE_WRITE_EX(device, ptr[i]); } } void writeVarString(const QString &value, QIODevice *device) { quint32 lenTag = value.length() != 4 ? value.length() : 0; SAFE_WRITE_EX(device, lenTag); if (!device->write(value.toAscii().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } void writePascalString(const QString &value, QIODevice *device) { quint8 lenTag = value.length(); SAFE_WRITE_EX(device, lenTag); if (!device->write(value.toAscii().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } void writeFixedString(const QString &value, QIODevice *device) { KIS_ASSERT_RECOVER_RETURN(value.length() == 4); if (!device->write(value.toAscii().data(), value.length())) { warnKrita << "WARNING: ASL: Failed to write ASL string" << ppVar(value); return; } } // Write UUID fetched from the file name or generate QString getPatternUuidLazy(const KoPattern *pattern) { QUuid uuid; QString patternFileName = pattern->filename(); if (patternFileName.endsWith(".pat", Qt::CaseInsensitive)) { QString strUuid = patternFileName.left(patternFileName.size() - 4); uuid = QUuid(strUuid); } if (uuid.isNull()) { warnKrita << "WARNING: Saved pattern doesn't have a UUID, generating..."; warnKrita << ppVar(patternFileName) << ppVar(pattern->name()); uuid = QUuid::createUuid(); } return uuid.toString().mid(1, 36); } } diff --git a/krita/libpsd/asl/kis_asl_xml_parser.h b/krita/libpsd/asl/kis_asl_xml_parser.h index dc65fc2165..c33b4f9838 100644 --- a/krita/libpsd/asl/kis_asl_xml_parser.h +++ b/krita/libpsd/asl/kis_asl_xml_parser.h @@ -1,35 +1,34 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ASL_XML_PARSER_H #define __KIS_ASL_XML_PARSER_H #include "kritapsd_export.h" class QDomDocument; -class QIODevice; class KisAslObjectCatcher; class KRITAPSD_EXPORT KisAslXmlParser { public: void parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher); }; #endif /* __KIS_ASL_XML_PARSER_H */ diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h index 8db8240bcf..07a5a5df99 100644 --- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h +++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h @@ -1,164 +1,165 @@ /* * kis_layer_box.h - part of Krita aka Krayon aka KimageShop * * Copyright (c) 2002 Patrick Julien * Copyright (C) 2006 Gábor Lehel * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007-2009 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_LAYERBOX_H #define KIS_LAYERBOX_H #include #include #include #include #include #include #include #include #include +#include "kis_action.h" #include "KisViewManager.h" #include "kis_mainwindow_observer.h" class QModelIndex; typedef QList QModelIndexList; class QMenu; class QAbstractButton; class KoCompositeOp; class KisCanvas2; class KisNodeModel; class Ui_WdgLayerBox; /** * A widget that visualized the layer structure. * */ class KisLayerBox : public QDockWidget, public KisMainwindowObserver { Q_OBJECT public: KisLayerBox(); virtual ~KisLayerBox(); QString observerName() { return "KisLayerBox"; } /// reimplemented from KisMainwindowObserver virtual void setMainWindow(KisViewManager* kisview); virtual void setCanvas(KoCanvasBase *canvas); virtual void unsetCanvas(); private Q_SLOTS: void notifyImageDeleted(); void slotContextMenuRequested(const QPoint &pos, const QModelIndex &index); void slotMinimalView(); void slotDetailedView(); void slotThumbnailView(); // From the node manager to the layerbox void slotSetCompositeOp(const KoCompositeOp* compositeOp); void slotSetOpacity(double opacity); void slotFillCompositeOps(const KoColorSpace * colorSpace); void updateUI(); void setCurrentNode(KisNodeSP node); void slotModelReset(); // from the layerbox to the node manager void slotRmClicked(); void slotRaiseClicked(); void slotLowerClicked(); void slotLeftClicked(); void slotRightClicked(); void slotPropertiesClicked(); void slotMergeLayer(); void slotCompositeOpChanged(int index); void slotOpacityChanged(); void slotOpacitySliderMoved(qreal opacity); void slotCollapsed(const QModelIndex &index); void slotExpanded(const QModelIndex &index); void slotSelectOpaque(); void slotNodeCollapsedChanged(); void slotEditGlobalSelection(bool showSelections); void selectionChanged(const QModelIndexList selection); void updateThumbnail(); private: inline void connectActionToButton(KisViewManager* view, QAbstractButton *button, const QString &id); inline void addActionToMenu(QMenu *menu, const QString &id); KisNodeSP findNonHidableNode(KisNodeSP startNode); private: KisCanvas2* m_canvas; QMenu *m_viewModeMenu; QMenu *m_newLayerMenu; KisImageWSP m_image; QPointer m_nodeModel; QPointer m_nodeManager; Ui_WdgLayerBox* m_wdgLayerBox; QTimer m_opacityDelayTimer; int m_newOpacity; QVector m_actions; KisAction* m_removeAction; KisAction* m_propertiesAction; KisAction* m_selectOpaque; }; class KisLayerBoxFactory : public KoDockFactoryBase { public: KisLayerBoxFactory() { } virtual QString id() const { return QString("KisLayerBox"); } virtual QDockWidget* createDockWidget() { KisLayerBox * dockWidget = new KisLayerBox(); dockWidget->setObjectName(id()); return dockWidget; } DockPosition defaultDockPosition() const { return DockRight; } }; #endif // KIS_LAYERBOX_H diff --git a/krita/plugins/extensions/layersplit/layersplit.cpp b/krita/plugins/extensions/layersplit/layersplit.cpp index 505397f182..f1bdc2f974 100644 --- a/krita/plugins/extensions/layersplit/layersplit.cpp +++ b/krita/plugins/extensions/layersplit/layersplit.cpp @@ -1,221 +1,222 @@ /* * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "layersplit.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "dlg_layersplit.h" #include "kis_node_manager.h" #include "kis_node_commands_adapter.h" #include "kis_undo_adapter.h" #include #include K_PLUGIN_FACTORY_WITH_JSON(LayerSplitFactory, "kritalayersplit.json", registerPlugin();) LayerSplit::LayerSplit(QObject *parent, const QVariantList &) : KisViewPlugin(parent) { KisAction *action = new KisAction(i18n("Split Layer..."), this); action->setActivationFlags(KisAction::ACTIVE_NODE); addAction("layersplit", action); connect(action, SIGNAL(triggered()), this, SLOT(slotLayerSplit())); } LayerSplit::~LayerSplit() { } struct Layer { KoColor color; KisPaintDeviceSP device; KisRandomAccessorSP accessor; int pixelsWritten; bool operator<(const Layer& other) const { return pixelsWritten < other.pixelsWritten; } }; void LayerSplit::slotLayerSplit() { DlgLayerSplit dlg; if (dlg.exec() == QDialog::Accepted) { dlg.hide(); QApplication::setOverrideCursor(Qt::WaitCursor); KoProgressUpdater* pu = m_view->createProgressUpdater(KoProgressUpdater::Unthreaded); pu->start(100, i18n("Split into Layers")); QPointer updater = pu->startSubtask(); KisImageSP image = m_view->image(); if (!image) return; image->lock(); KisNodeSP node = m_view->activeNode(); if (!node) return; KisPaintDeviceSP projection = node->projection(); if (!projection) return; QList colorMap; const KoColorSpace *cs = projection->colorSpace(); QRect rc = image->bounds(); int fuzziness = dlg.fuzziness(); updater->setProgress(0); KisRandomConstAccessorSP acc = projection->createRandomConstAccessorNG(rc.x(), rc.y()); for (int row = rc.y(); row < rc.height(); ++row) { for (int col = rc.x(); col < rc.width(); ++col) { acc->moveTo(col, row); KoColor c(cs); c.setColor(acc->rawDataConst(), cs); if (c.opacityU8() == OPACITY_TRANSPARENT_U8) { continue; } if (dlg.disregardOpacity()) { c.setOpacity(OPACITY_OPAQUE_U8); } bool found = false; foreach(const Layer &l, colorMap) { if (fuzziness == 0) { found = (l.color == c); } else { quint8 match = cs->difference(l.color.data(), c.data()); found = (match <= fuzziness); } if (found) { KisRandomAccessorSP dstAcc = l.accessor; dstAcc->moveTo(col, row); memcpy(dstAcc->rawData(), acc->rawDataConst(), cs->pixelSize()); const_cast(&l)->pixelsWritten++; break; } } if (!found) { Layer l; l.color = c; l.device = new KisPaintDevice(cs, KoColor::toQString(c)); l.accessor = l.device->createRandomAccessorNG(col, row); l.accessor->moveTo(col, row); memcpy(l.accessor->rawData(), acc->rawDataConst(), cs->pixelSize()); l.pixelsWritten = 1; colorMap << l; } } if (updater->interrupted()) { return; } updater->setProgress((row - rc.y()) * 100 / rc.height() - rc.y()); } updater->setProgress(100); dbgKrita << "Created" << colorMap.size() << "layers"; // foreach(const Layer &l, colorMap) { // dbgKrita << "\t" << l.device->objectName() << ":" << l.pixelsWritten; // } if (dlg.sortLayers()) { qSort(colorMap); } KisUndoAdapter *undo = image->undoAdapter(); undo->beginMacro(kundo2_i18n("Split Layer")); KisNodeCommandsAdapter adapter(m_view); KisGroupLayerSP baseGroup = dynamic_cast(node->parent().data()); if (!baseGroup) { // Masks are never nested baseGroup = dynamic_cast(node->parent()->parent().data()); } if (dlg.hideOriginal()) { node->setVisible(false); } if (dlg.createBaseGroup()) { KisGroupLayerSP grp = new KisGroupLayer(image, i18n("Color"), OPACITY_OPAQUE_U8); adapter.addNode(grp, baseGroup, 1); baseGroup = grp; } foreach(const Layer &l, colorMap) { KisGroupLayerSP grp = baseGroup; if (dlg.createSeparateGroups()) { grp = new KisGroupLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8); adapter.addNode(grp, baseGroup, 1); } KisPaintLayerSP paintLayer = new KisPaintLayer(image, l.device->objectName(), OPACITY_OPAQUE_U8, l.device); adapter.addNode(paintLayer, grp, 0); paintLayer->setAlphaLocked(dlg.lockAlpha()); } undo->endMacro(); image->unlock(); image->setModified(); } QApplication::restoreOverrideCursor(); } #include "layersplit.moc" diff --git a/krita/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc b/krita/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc index 60a266b497..e5f43cf0fb 100644 --- a/krita/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc +++ b/krita/plugins/paintops/defaultpaintops/defaultpaintops_plugin.cc @@ -1,59 +1,60 @@ /* * defaultpaintops_plugin.cc -- Part of Krita * * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "defaultpaintops_plugin.h" #include #include #include #include #include "kis_simple_paintop_factory.h" #include "kis_brushop.h" #include "kis_brushop_settings_widget.h" #include "kis_duplicateop.h" #include "kis_duplicateop_settings.h" #include "kis_global.h" #include "kis_paintop_registry.h" #include "kis_brush_based_paintop_settings.h" #include "kis_brush_server.h" +#include "kis_duplicateop_settings_widget.h" K_PLUGIN_FACTORY_WITH_JSON(DefaultPaintOpsPluginFactory, "kritadefaultpaintops.json", registerPlugin();) DefaultPaintOpsPlugin::DefaultPaintOpsPlugin(QObject *parent, const QVariantList &) : QObject(parent) { KisPaintOpRegistry *r = KisPaintOpRegistry::instance(); r->add(new KisSimplePaintOpFactory( "paintbrush", i18nc("Pixel paintbrush", "Pixel"), KisPaintOpFactory::categoryStable(), "krita-paintbrush.png", QString(), QStringList(), 1)); r->add(new KisSimplePaintOpFactory("duplicate", i18nc("clone paintbrush (previously \"Duplicate\")", "Clone"), KisPaintOpFactory::categoryStable(), "krita-duplicate.png", QString(), QStringList(COMPOSITE_COPY), 15)); QStringList whiteList; whiteList << COMPOSITE_COPY; KisBrushServer::instance(); } DefaultPaintOpsPlugin::~DefaultPaintOpsPlugin() { } #include "defaultpaintops_plugin.moc" diff --git a/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h b/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h index 2fbcbe36f8..7ee846d5ee 100644 --- a/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h +++ b/krita/plugins/paintops/defaultpaintops/duplicate/kis_duplicateop_settings.h @@ -1,68 +1,66 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DUPLICATEOP_SETTINGS_H_ #define KIS_DUPLICATEOP_SETTINGS_H_ #include #include #include -#include "kis_duplicateop_settings_widget.h" - class QDomElement; class KisDuplicateOpSettings : public KisBrushBasedPaintOpSettings { public: using KisPaintOpSettings::fromXML; using KisPaintOpSettings::toXML; KisDuplicateOpSettings(); virtual ~KisDuplicateOpSettings(); bool paintIncremental(); QString indirectPaintingCompositeOp() const; QPointF offset() const; QPointF position() const; virtual bool mousePressEvent(const KisPaintInformation& pos, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode); void activate(); void fromXML(const QDomElement& elt); void toXML(QDomDocument& doc, QDomElement& rootElt) const; KisPaintOpSettingsSP clone() const; QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const; KisNodeWSP sourceNode() const; public: QPointF m_offset; bool m_isOffsetNotUptodate; QPointF m_position; // Give the position of the last alt-click KisNodeWSP m_sourceNode; }; #endif // KIS_DUPLICATEOP_SETTINGS_H_ diff --git a/krita/plugins/paintops/deform/kis_deform_paintop.cpp b/krita/plugins/paintops/deform/kis_deform_paintop.cpp index 0b1a9a3699..89bf7cc237 100644 --- a/krita/plugins/paintops/deform/kis_deform_paintop.cpp +++ b/krita/plugins/paintops/deform/kis_deform_paintop.cpp @@ -1,154 +1,152 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_deform_paintop.h" #include "kis_deform_paintop_settings.h" #include #include #include #include #include #include "kis_global.h" #include "kis_paint_device.h" #include "kis_painter.h" -#include "kis_types.h" -#include "kis_paintop.h" #include "kis_selection.h" #include "kis_random_accessor_ng.h" #include #include "kis_deform_option.h" #include "kis_brush_size_option.h" #include #include #ifdef Q_OS_WIN // quoting DRAND48(3) man-page: // These functions are declared obsolete by SVID 3, // which states that rand(3) should be used instead. #define drand48() (static_cast(qrand()) / static_cast(RAND_MAX)) #endif KisDeformPaintOp::KisDeformPaintOp(const KisDeformPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) { Q_UNUSED(image); Q_UNUSED(node); Q_ASSERT(settings); m_sizeProperties.readOptionSetting(settings); // sensors m_sizeOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_properties.action = settings->getInt(DEFORM_ACTION); m_properties.deformAmount = settings->getDouble(DEFORM_AMOUNT); m_properties.useBilinear = settings->getBool(DEFORM_USE_BILINEAR); m_properties.useCounter = settings->getBool(DEFORM_USE_COUNTER); m_properties.useOldData = settings->getBool(DEFORM_USE_OLD_DATA); m_deformBrush.setProperties(&m_properties); m_deformBrush.setSizeProperties(&m_sizeProperties); m_deformBrush.initDeformAction(); m_dev = source(); if ((m_sizeProperties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_sizeProperties.diameter * 0.5 * m_sizeProperties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisDeformPaintOp::~KisDeformPaintOp() { } KisSpacingInformation KisDeformPaintOp::paintAt(const KisPaintInformation& info) { if (!painter()) return KisSpacingInformation(m_spacing); if (!m_dev) return KisSpacingInformation(m_spacing); KisFixedPaintDeviceSP dab = cachedDab(source()->compositionSourceColorSpace()); qint32 x; qreal subPixelX; qint32 y; qreal subPixelY; QPointF pt = info.pos(); if (m_sizeProperties.jitterEnabled) { pt.setX(pt.x() + ((m_sizeProperties.diameter * drand48()) - m_sizeProperties.diameter * 0.5) * m_sizeProperties.jitterMovementAmount); pt.setY(pt.y() + ((m_sizeProperties.diameter * drand48()) - m_sizeProperties.diameter * 0.5) * m_sizeProperties.jitterMovementAmount); } qreal rotation = m_rotationOption.apply(info); // Deform Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(info); setCurrentRotation(rotation); setCurrentScale(scale); rotation += m_sizeProperties.rotation; scale *= m_sizeProperties.scale; QPointF pos = pt - m_deformBrush.hotSpot(scale, rotation); splitCoordinate(pos.x(), &x, &subPixelX); splitCoordinate(pos.y(), &y, &subPixelY); KisFixedPaintDeviceSP mask = m_deformBrush.paintMask(dab, m_dev, scale, rotation, info.pos(), subPixelX, subPixelY, x, y ); // this happens for the first dab of the move mode, we need more information for being able to move if (!mask) { return KisSpacingInformation(m_spacing); } quint8 origOpacity = m_opacityOption.apply(painter(), info); painter()->bltFixedWithFixedSelection(x, y, dab, mask, mask->bounds().width() , mask->bounds().height()); painter()->renderMirrorMask(QRect(QPoint(x, y), QSize(mask->bounds().width() , mask->bounds().height())), dab, mask); painter()->setOpacity(origOpacity); return KisSpacingInformation(m_spacing); } diff --git a/krita/plugins/paintops/dynadraw/kis_dyna_paintop.h b/krita/plugins/paintops/dynadraw/kis_dyna_paintop.h index ed5cbe61a9..9e6a3ab796 100644 --- a/krita/plugins/paintops/dynadraw/kis_dyna_paintop.h +++ b/krita/plugins/paintops/dynadraw/kis_dyna_paintop.h @@ -1,53 +1,53 @@ /* * Copyright (c) 2009-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DYNA_PAINTOP_H_ #define KIS_DYNA_PAINTOP_H_ #include #include #include #include "dyna_brush.h" -#include "kis_dyna_paintop_settings.h" class KisPainter; +class KisDynaPaintOpSettings; class KisDynaPaintOp : public KisPaintOp { public: KisDynaPaintOp(const KisDynaPaintOpSettings *settings, KisPainter * painter, KisNodeSP node, KisImageSP image); ~KisDynaPaintOp(); KisSpacingInformation paintAt(const KisPaintInformation& info); void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance); virtual bool incremental() const { return true; } private: KisDynaProperties m_properties; KisPaintDeviceSP m_dab; DynaBrush m_dynaBrush; }; #endif // KIS_DYNA_PAINTOP_H_ diff --git a/krita/plugins/paintops/filterop/kis_filterop_settings.cpp b/krita/plugins/paintops/filterop/kis_filterop_settings.cpp index d09bd30d2f..4fa6b281f2 100644 --- a/krita/plugins/paintops/filterop/kis_filterop_settings.cpp +++ b/krita/plugins/paintops/filterop/kis_filterop_settings.cpp @@ -1,91 +1,90 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004-2008 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_filterop_settings.h" #include -#include "kis_filterop_settings_widget.h" #include #include #include #include #include #include #include #include KisFilterOpSettings::KisFilterOpSettings() { setPropertyNotSaved(FILTER_CONFIGURATION); } KisFilterOpSettings::~KisFilterOpSettings() { } bool KisFilterOpSettings::paintIncremental() { return true; // We always paint on the existing data } KisFilterConfiguration* KisFilterOpSettings::filterConfig() const { if (hasProperty(FILTER_ID)) { KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID)); if (filter) { KisFilterConfiguration* configuration = filter->factoryConfiguration(0); configuration->fromXML(getString(FILTER_CONFIGURATION)); return configuration; } } return 0; } void KisFilterOpSettings::toXML(QDomDocument& doc, QDomElement& root) const { KisPaintOpSettings::toXML(doc, root); KisFilterConfiguration* configuration = filterConfig(); if (configuration) { QDomElement e = doc.createElement("filterconfig"); configuration->toXML(doc, e); root.appendChild(e); } delete configuration; } void KisFilterOpSettings::fromXML(const QDomElement& e) { KisPaintOpSettings::fromXML(e); QDomElement element = e.firstChildElement("filterconfig"); if (hasProperty(FILTER_ID)) { KisFilterSP filter = KisFilterRegistry::instance()->get(getString(FILTER_ID)); if (filter) { KisFilterConfiguration* configuration = filter->factoryConfiguration(0); configuration->fromXML(element); setProperty(FILTER_CONFIGURATION, configuration->toXML()); delete configuration; } } } diff --git a/krita/plugins/paintops/hairy/hairy_brush.cpp b/krita/plugins/paintops/hairy/hairy_brush.cpp index c5a0244506..0dcb89e582 100644 --- a/krita/plugins/paintops/hairy/hairy_brush.cpp +++ b/krita/plugins/paintops/hairy/hairy_brush.cpp @@ -1,467 +1,466 @@ /* * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if defined(_WIN32) || defined(_WIN64) #include #define srand48 srand inline double drand48() { return double(rand()) / RAND_MAX; } #endif #include "hairy_brush.h" -#include "trajectory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include HairyBrush::HairyBrush() { srand48(time(0)); m_counter = 0; m_lastAngle = 0.0; m_oldPressure = 1.0f; m_saturationId = -1; m_transfo = 0; } HairyBrush::~HairyBrush() { delete m_transfo; qDeleteAll(m_bristles.begin(), m_bristles.end()); m_bristles.clear(); } void HairyBrush::initAndCache() { m_compositeOp = m_dab->colorSpace()->compositeOp(COMPOSITE_OVER); m_pixelSize = m_dab->colorSpace()->pixelSize(); if (m_properties->useSaturation) { m_transfo = m_dab->colorSpace()->createColorTransformation("hsv_adjustment", m_params); if (m_transfo) { m_saturationId = m_transfo->parameterId("s"); } } } void HairyBrush::fromDabWithDensity(KisFixedPaintDeviceSP dab, qreal density) { int width = dab->bounds().width(); int height = dab->bounds().height(); int centerX = width * 0.5; int centerY = height * 0.5; // make mask Bristle * bristle = 0; qreal alpha; quint8 * dabPointer = dab->data(); quint8 pixelSize = dab->pixelSize(); const KoColorSpace * cs = dab->colorSpace(); KoColor bristleColor(cs); srand48(12345678); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { alpha = cs->opacityF(dabPointer); if (alpha != 0.0) { if (density == 1.0 || drand48() <= density) { memcpy(bristleColor.data(), dabPointer, pixelSize); bristle = new Bristle(x - centerX, y - centerY, alpha); // using value from image as length of bristle bristle->setColor(bristleColor); m_bristles.append(bristle); } } dabPointer += pixelSize; } } } void HairyBrush::paintLine(KisPaintDeviceSP dab, KisPaintDeviceSP layer, const KisPaintInformation &pi1, const KisPaintInformation &pi2, qreal scale, qreal rotation) { m_counter++; qreal x1 = pi1.pos().x(); qreal y1 = pi1.pos().y(); qreal x2 = pi2.pos().x(); qreal y2 = pi2.pos().y(); qreal dx = x2 - x1; qreal dy = y2 - y1; // TODO:this angle is different from the drawing angle in sensor (info.angle()). The bug is caused probably due to // not computing the drag vector properly in paintBezierLine when smoothing is used //qreal angle = atan2(dy, dx); qreal angle = rotation; qreal mousePressure = 1.0; if (m_properties->useMousePressure) { // want pressure from mouse movement qreal distance = sqrt(dx * dx + dy * dy); mousePressure = (1.0 - computeMousePressure(distance)); scale *= mousePressure; } // this pressure controls shear and ink depletion qreal pressure = mousePressure * (pi2.pressure() * 2); Bristle *bristle = 0; KoColor bristleColor(dab->colorSpace()); m_dabAccessor = dab->createRandomAccessorNG((int)x1, (int)y1); m_dab = dab; // initialization block if (firstStroke()) { initAndCache(); } // if this is first time the brush touches the canvas and we use soak the ink from canvas if (firstStroke() && m_properties->useSoakInk) { if (layer) { colorifyBristles(layer, pi1.pos()); } else { dbgKrita << "Can't soak the ink from the layer"; } } qreal fx1, fy1, fx2, fy2; qreal randomX, randomY; qreal shear; float inkDeplation = 0.0; int inkDepletionSize = m_properties->inkDepletionCurve.size(); int bristleCount = m_bristles.size(); int bristlePathSize; qreal treshold = 1.0 - pi2.pressure(); for (int i = 0; i < bristleCount; i++) { if (!m_bristles.at(i)->enabled()) continue; bristle = m_bristles[i]; randomX = (drand48() * 2 - 1.0) * m_properties->randomFactor; randomY = (drand48() * 2 - 1.0) * m_properties->randomFactor; shear = pressure * m_properties->shearFactor; m_transform.reset(); m_transform.rotateRadians(-angle); m_transform.scale(scale, scale); m_transform.translate(randomX, randomY); m_transform.shear(shear, shear); if (firstStroke() || (!m_properties->connectedPath)) { // transform start dab m_transform.map(bristle->x(), bristle->y(), &fx1, &fy1); // transform end dab m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2); } else { // continue the path of the bristle from the previous position fx1 = bristle->prevX(); fy1 = bristle->prevY(); m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2); } // remember the end point bristle->setPrevX(fx2); bristle->setPrevY(fy2); // all coords relative to device position fx1 += x1; fy1 += y1; fx2 += x2; fy2 += y2; if (m_properties->threshold && (bristle->length() < treshold)) continue; // paint between first and last dab const QVector bristlePath = m_trajectory.getLinearTrajectory(QPointF(fx1, fy1), QPointF(fx2, fy2), 1.0); bristlePathSize = m_trajectory.size(); memcpy(bristleColor.data(), bristle->color().data() , m_pixelSize); for (int i = 0; i < bristlePathSize ; i++) { if (m_properties->inkDepletionEnabled) { inkDeplation = fetchInkDepletion(bristle, inkDepletionSize); if (m_properties->useSaturation && m_transfo != 0) { saturationDepletion(bristle, bristleColor, pressure, inkDeplation); } if (m_properties->useOpacity) { opacityDepletion(bristle, bristleColor, pressure, inkDeplation); } } else { if (bristleColor.opacityU8() != 0) { bristleColor.setOpacity(bristle->length()); } } addBristleInk(bristle, bristlePath.at(i), bristleColor); bristle->setInkAmount(1.0 - inkDeplation); bristle->upIncrement(); } } //repositionBristles(angle,slope); m_dab = 0; m_dabAccessor = 0; } inline qreal HairyBrush::fetchInkDepletion(Bristle* bristle, int inkDepletionSize) { if (bristle->counter() >= inkDepletionSize - 1) { return m_properties->inkDepletionCurve[inkDepletionSize - 1]; } else { return m_properties->inkDepletionCurve[bristle->counter()]; } } void HairyBrush::saturationDepletion(Bristle * bristle, KoColor &bristleColor, qreal pressure, qreal inkDeplation) { qreal saturation; if (m_properties->useWeights) { // new weighted way (experiment) saturation = ( (pressure * m_properties->pressureWeight) + (bristle->length() * m_properties->bristleLengthWeight) + (bristle->inkAmount() * m_properties->bristleInkAmountWeight) + ((1.0 - inkDeplation) * m_properties->inkDepletionWeight)) - 1.0; } else { // old way of computing saturation saturation = ( pressure * bristle->length() * bristle->inkAmount() * (1.0 - inkDeplation)) - 1.0; } m_transfo->setParameter(m_transfo->parameterId("h"), 0.0); m_transfo->setParameter(m_transfo->parameterId("v"), 0.0); m_transfo->setParameter(m_saturationId, saturation); m_transfo->setParameter(3, 1);//sets the type to m_transfo->setParameter(4, false);//sets the colorize to none. m_transfo->transform(bristleColor.data(), bristleColor.data() , 1); } void HairyBrush::opacityDepletion(Bristle* bristle, KoColor& bristleColor, qreal pressure, qreal inkDeplation) { qreal opacity = OPACITY_OPAQUE_F; if (m_properties->useWeights) { opacity = qBound(0.0, (pressure * m_properties->pressureWeight) + (bristle->length() * m_properties->bristleLengthWeight) + (bristle->inkAmount() * m_properties->bristleInkAmountWeight) + ((1.0 - inkDeplation) * m_properties->inkDepletionWeight), 1.0); } else { opacity = bristle->length() * bristle->inkAmount(); } bristleColor.setOpacity(opacity); } void HairyBrush::repositionBristles(double angle, double slope) { // setX srand48((int)slope); for (int i = 0; i < m_bristles.size(); i++) { float x = m_bristles[i]->x(); m_bristles[i]->setX(x + drand48()); } // setY srand48((int)angle); for (int i = 0; i < m_bristles.size(); i++) { float y = m_bristles[i]->y(); m_bristles[i]->setY(y + drand48()); } } inline void HairyBrush::addBristleInk(Bristle *bristle,const QPointF &pos, const KoColor &color) { Q_UNUSED(bristle); if (m_properties->antialias) { if (m_properties->useCompositing) { paintParticle(pos, color); } else { paintParticle(pos, color, 1.0); } } else { int ix = qRound(pos.x()); int iy = qRound(pos.y()); if (m_properties->useCompositing) { plotPixel(ix, iy, color); } else { darkenPixel(ix, iy, color); } } } void HairyBrush::paintParticle(QPointF pos, const KoColor& color, qreal weight) { // opacity top left, right, bottom left, right quint8 opacity = color.opacityU8(); opacity *= weight; int ipx = int (pos.x()); int ipy = int (pos.y()); qreal fx = pos.x() - ipx; qreal fy = pos.y() - ipy; quint8 btl = qRound((1.0 - fx) * (1.0 - fy) * opacity); quint8 btr = qRound((fx) * (1.0 - fy) * opacity); quint8 bbl = qRound((1.0 - fx) * (fy) * opacity); quint8 bbr = qRound((fx) * (fy) * opacity); const KoColorSpace * cs = m_dab->colorSpace(); m_dabAccessor->moveTo(ipx , ipy); btl = quint8(qBound(OPACITY_TRANSPARENT_U8, btl + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8)); memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize()); cs->setOpacity(m_dabAccessor->rawData(), btl, 1); m_dabAccessor->moveTo(ipx + 1, ipy); btr = quint8(qBound(OPACITY_TRANSPARENT_U8, btr + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8)); memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize()); cs->setOpacity(m_dabAccessor->rawData(), btr, 1); m_dabAccessor->moveTo(ipx, ipy + 1); bbl = quint8(qBound(OPACITY_TRANSPARENT_U8, bbl + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8)); memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize()); cs->setOpacity(m_dabAccessor->rawData(), bbl, 1); m_dabAccessor->moveTo(ipx + 1, ipy + 1); bbr = quint8(qBound(OPACITY_TRANSPARENT_U8, bbr + cs->opacityU8(m_dabAccessor->rawData()), OPACITY_OPAQUE_U8)); memcpy(m_dabAccessor->rawData(), color.data(), cs->pixelSize()); cs->setOpacity(m_dabAccessor->rawData(), bbr, 1); } void HairyBrush::paintParticle(QPointF pos, const KoColor& color) { // opacity top left, right, bottom left, right memcpy(m_color.data(), color.data(), m_pixelSize); quint8 opacity = color.opacityU8(); int ipx = int (pos.x()); int ipy = int (pos.y()); qreal fx = pos.x() - ipx; qreal fy = pos.y() - ipy; quint8 btl = qRound((1.0 - fx) * (1.0 - fy) * opacity); quint8 btr = qRound((fx) * (1.0 - fy) * opacity); quint8 bbl = qRound((1.0 - fx) * (fy) * opacity); quint8 bbr = qRound((fx) * (fy) * opacity); m_color.setOpacity(btl); plotPixel(ipx , ipy, m_color); m_color.setOpacity(btr); plotPixel(ipx + 1 , ipy, m_color); m_color.setOpacity(bbl); plotPixel(ipx , ipy + 1, m_color); m_color.setOpacity(bbr); plotPixel(ipx + 1 , ipy + 1, m_color); } inline void HairyBrush::plotPixel(int wx, int wy, const KoColor &color) { m_dabAccessor->moveTo(wx, wy); m_compositeOp->composite(m_dabAccessor->rawData(), m_pixelSize, color.data() , m_pixelSize, 0, 0, 1, 1, OPACITY_OPAQUE_U8); } inline void HairyBrush::darkenPixel(int wx, int wy, const KoColor &color) { m_dabAccessor->moveTo(wx, wy); if (m_dab->colorSpace()->opacityU8(m_dabAccessor->rawData()) < color.opacityU8()) { memcpy(m_dabAccessor->rawData(), color.data(), m_pixelSize); } } double HairyBrush::computeMousePressure(double distance) { static const double scale = 20.0; static const double minPressure = 0.02; double oldPressure = m_oldPressure; double factor = 1.0 - distance / scale; if (factor < 0.0) factor = 0.0; double result = ((4.0 * oldPressure) + minPressure + factor) / 5.0; m_oldPressure = result; return result; } void HairyBrush::colorifyBristles(KisPaintDeviceSP source, QPointF point) { KoColor bristleColor(m_dab->colorSpace()); KisCrossDeviceColorPickerInt colorPicker(source, bristleColor); Bristle *b = 0; int size = m_bristles.size(); for (int i = 0; i < size; i++) { b = m_bristles[i]; int x = qRound(b->x() + point.x()); int y = qRound(b->y() + point.y()); colorPicker.pickOldColor(x, y, bristleColor.data()); b->setColor(bristleColor); } } diff --git a/krita/plugins/paintops/hairy/kis_hairy_paintop_settings.h b/krita/plugins/paintops/hairy/kis_hairy_paintop_settings.h index dc1537c849..938564ee70 100644 --- a/krita/plugins/paintops/hairy/kis_hairy_paintop_settings.h +++ b/krita/plugins/paintops/hairy/kis_hairy_paintop_settings.h @@ -1,41 +1,40 @@ /* * Copyright (c) 2008 Boudewijn Rempt * Copyright (c) 2008-2010 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_HAIRYPAINTOP_SETTINGS_H_ #define KIS_HAIRYPAINTOP_SETTINGS_H_ #include #include #include -class QPainter; class KisHairyPaintOpSettings : public KisBrushBasedPaintOpSettings { public: using KisPaintOpSettings::fromXML; KisHairyPaintOpSettings(); QPainterPath brushOutline(const KisPaintInformation &info, OutlineMode mode) const; }; #endif diff --git a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index 2f5885bf6e..d6078e8a71 100644 --- a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,169 +1,168 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_based_paintop.h" -#include "kis_brush.h" #include "kis_properties_configuration.h" #include "kis_brush_option.h" #include #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(const KisPropertiesConfiguration *settings) { if (KisBrushOption::isTextBrush(settings)) { KisBrushOption brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfiguration *settings) { return settings && settings == m_settings ? m_brush : 0; } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(const KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings.data()); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfiguration* settings, KisPainter* painter) : KisPaintOp(painter) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOption brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); } m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return (scale * m_brush->width() < 0.01 || scale * m_brush->height() < 0.01); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(scale, scale, 0); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, rotation); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { qreal extraSpacingScale = 1.0; if (spacingOption.isChecked()) { extraSpacingScale = spacingOption.apply(pi); } // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(scale, scale, 0); return effectiveSpacing(metric.width(), metric.height(), extraSpacingScale, spacingOption.isotropicSpacing(), rotation); } inline qreal calcAutoSpacing(qreal value, qreal coeff) { return coeff * (value < 1.0 ? value : sqrt(value)); } inline QPointF calcAutoSpacing(const QPointF &pt, qreal coeff) { return QPointF(calcAutoSpacing(pt.x(), coeff), calcAutoSpacing(pt.y(), coeff)); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation) const { QPointF spacing; if (!isotropicSpacing) { if (m_brush->autoSpacingActive()) { spacing = calcAutoSpacing(QPointF(dabWidth, dabHeight), m_brush->autoSpacingCoeff()); } else { spacing = QPointF(dabWidth, dabHeight); spacing *= m_brush->spacing(); } } else { qreal significantDimension = qMax(dabWidth, dabHeight); if (m_brush->autoSpacingActive()) { significantDimension = calcAutoSpacing(significantDimension, m_brush->autoSpacingCoeff()); } else { significantDimension *= m_brush->spacing(); } spacing = QPointF(significantDimension, significantDimension); rotation = 0.0; } spacing *= extraScale; return KisSpacingInformation(spacing, rotation); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.h b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.h index 32cf2ba1b4..bceb9559a6 100644 --- a/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.h +++ b/krita/plugins/paintops/libpaintop/kis_brush_based_paintop.h @@ -1,91 +1,92 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_BRUSH_BASED_PAINTOP_H #define KIS_BRUSH_BASED_PAINTOP_H #include "kritapaintop_export.h" #include "kis_paintop.h" #include "kis_dab_cache.h" #include "kis_brush.h" #include "kis_texture_option.h" #include "kis_precision_option.h" #include "kis_pressure_mirror_option.h" #include class KisPropertiesConfiguration; class KisPressureSpacingOption; +class KisDabCache; /// Internal class TextBrushInitializationWorkaround { public: TextBrushInitializationWorkaround(); ~TextBrushInitializationWorkaround(); static TextBrushInitializationWorkaround* instance(); void preinitialize(const KisPropertiesConfiguration *settings); KisBrushSP tryGetBrush(const KisPropertiesConfiguration *settings); private: KisBrushSP m_brush; const KisPropertiesConfiguration *m_settings; }; /** * This is a base class for paintops that use a KisBrush or derived * brush to paint with. This is mainly important for the spacing * generation. */ class PAINTOP_EXPORT KisBrushBasedPaintOp : public KisPaintOp { public: KisBrushBasedPaintOp(const KisPropertiesConfiguration* settings, KisPainter* painter); ~KisBrushBasedPaintOp(); bool checkSizeTooSmall(qreal scale); KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation) const; KisSpacingInformation effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const; ///Reimplemented, false if brush is 0 virtual bool canPaint() const; #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND typedef int needs_preinitialization; static void preinitializeOpStatically(const KisPaintOpSettingsSP settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ private: KisSpacingInformation effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation) const; protected: // XXX: make private! KisBrushSP m_brush; KisTextureProperties m_textureProperties; KisPressureMirrorOption m_mirrorOption; KisPrecisionOption m_precisionOption; KisDabCache *m_dabCache; }; #endif diff --git a/krita/plugins/paintops/spray/kis_spray_paintop.cpp b/krita/plugins/paintops/spray/kis_spray_paintop.cpp index afdedf60eb..01715c128e 100644 --- a/krita/plugins/paintops/spray/kis_spray_paintop.cpp +++ b/krita/plugins/paintops/spray/kis_spray_paintop.cpp @@ -1,129 +1,130 @@ /* * Copyright (c) 2008-2012 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_spray_paintop.h" #include "kis_spray_paintop_settings.h" #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include KisSprayPaintOp::KisSprayPaintOp(const KisSprayPaintOpSettings *settings, KisPainter *painter, KisNodeSP node, KisImageSP image) : KisPaintOp(painter) , m_settings(settings) , m_isPresetValid(true) , m_node(node) { Q_ASSERT(settings); Q_ASSERT(painter); Q_UNUSED(image); m_rotationOption.readOptionSetting(settings); m_opacityOption.readOptionSetting(settings); m_sizeOption.readOptionSetting(settings); m_rotationOption.resetAllSensors(); m_opacityOption.resetAllSensors(); m_sizeOption.resetAllSensors(); m_brushOption.readOptionSetting(settings); m_colorProperties.fillProperties(settings); m_properties.loadSettings(settings); // first load tip properties as shape properties are dependent on diameter/scale/aspect m_shapeProperties.loadSettings(settings, m_properties.diameter * m_properties.scale, m_properties.diameter * m_properties.aspect * m_properties.scale); // TODO: what to do with proportional sizes? m_shapeDynamicsProperties.loadSettings(settings); if (!m_shapeProperties.enabled && !m_brushOption.brush()) { // in case the preset does not contain the definition for KisBrush m_isPresetValid = false; dbgKrita << "Preset is not valid. Painting is not possible. Use the preset editor to fix current brush engine preset."; } m_sprayBrush.setProperties(&m_properties, &m_colorProperties, &m_shapeProperties, &m_shapeDynamicsProperties, m_brushOption.brush()); m_sprayBrush.setFixedDab(cachedDab()); // spacing if ((m_properties.diameter * 0.5) > 1) { m_ySpacing = m_xSpacing = m_properties.diameter * 0.5 * m_properties.spacing; } else { m_ySpacing = m_xSpacing = 1.0; } m_spacing = m_xSpacing; } KisSprayPaintOp::~KisSprayPaintOp() { } KisSpacingInformation KisSprayPaintOp::paintAt(const KisPaintInformation& info) { if (!painter() || !m_isPresetValid) { return KisSpacingInformation(m_spacing); } if (!m_dab) { m_dab = source()->createCompositionSourceDevice(); } else { m_dab->clear(); } qreal rotation = m_rotationOption.apply(info); quint8 origOpacity = m_opacityOption.apply(painter(), info); // Spray Brush is capable of working with zero scale, // so no additional checks for 'zero'ness are needed qreal scale = m_sizeOption.apply(info); setCurrentRotation(rotation); setCurrentScale(scale); m_sprayBrush.paint(m_dab, m_node->paintDevice(), info, rotation, scale, painter()->paintColor(), painter()->backgroundColor()); QRect rc = m_dab->extent(); painter()->bitBlt(rc.topLeft(), m_dab, rc); painter()->renderMirrorMask(rc, m_dab); painter()->setOpacity(origOpacity); return KisSpacingInformation(m_spacing); } diff --git a/krita/plugins/paintops/spray/spray_brush.h b/krita/plugins/paintops/spray/spray_brush.h index c20f872a87..46cacf489b 100644 --- a/krita/plugins/paintops/spray/spray_brush.h +++ b/krita/plugins/paintops/spray/spray_brush.h @@ -1,113 +1,111 @@ /* * Copyright (c) 2008-2010,2013 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _SPRAY_BRUSH_H_ #define _SPRAY_BRUSH_H_ #include #include "kis_types.h" #include "kis_painter.h" #include "kis_color_option.h" #include "kis_spray_shape_option.h" #include "kis_spray_shape_dynamics.h" #include "kis_sprayop_option.h" -#include "random_gauss.h" - #include #include class KisPaintInformation; -class KisBrush; +class RandomGauss; class SprayBrush { public: SprayBrush(); ~SprayBrush(); void paint(KisPaintDeviceSP dab, KisPaintDeviceSP source, const KisPaintInformation& info, qreal rotation, qreal scale, const KoColor &color, const KoColor &bgColor); void setProperties(KisSprayProperties * properties, KisColorProperties * colorProperties, KisShapeProperties * shapeProperties, KisShapeDynamicsProperties * shapeDynamicsProperties, KisBrushSP brush) { m_properties = properties; m_colorProperties = colorProperties; m_shapeProperties = shapeProperties; m_shapeDynamicsProperties = shapeDynamicsProperties; m_brush = brush; } void setFixedDab(KisFixedPaintDeviceSP dab); private: KoColor m_inkColor; qreal m_radius; quint32 m_particlesCount; quint8 m_dabPixelSize; RandomGauss * m_rand; KisPainter * m_painter; KisPaintDeviceSP m_imageDevice; QImage m_brushQImage; QImage m_transformed; KoColorTransformation* m_transfo; const KisSprayProperties * m_properties; const KisColorProperties * m_colorProperties; const KisShapeProperties * m_shapeProperties; const KisShapeDynamicsProperties * m_shapeDynamicsProperties; KisBrushSP m_brush; KisFixedPaintDeviceSP m_fixedDab; private: /// rotation in radians according the settings (gauss distribution, uniform distribution or fixed angle) qreal rotationAngle(); /// Paints Wu Particle void paintParticle(KisRandomAccessorSP &writeAccessor, const KoColor &color, qreal rx, qreal ry); void paintCircle(KisPainter * painter, qreal x, qreal y, int radius); void paintEllipse(KisPainter * painter, qreal x, qreal y, int a, int b, qreal angle, int steps = 128); void paintRectangle(KisPainter * painter, qreal x, qreal y, int width, int height, qreal angle); void paintOutline(KisPaintDeviceSP dev, const KoColor& painterColor, qreal posX, qreal posY, qreal radius); /// mix a with b.b mix with weight and a with 1.0 - weight inline qreal linearInterpolation(qreal a, qreal b, qreal weight) const { return (1.0 - weight) * a + weight * b; } // TODO: move this somewhere where I can reuse it /// convert radians to degrees inline qreal rad2deg(qreal rad) const { return rad * (180.0 / M_PI); } /// convert degrees to radians inline qreal deg2rad(quint16 deg) const { return deg * (M_PI / 180.0); } }; #endif diff --git a/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h b/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h index d5dc1e18a8..e792760f37 100644 --- a/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h +++ b/krita/plugins/paintops/tangentnormal/kis_tangent_tilt_option.h @@ -1,69 +1,68 @@ /* This file is part of the KDE project * * Copyright (C) 2015 Wolthera van Hövell tot Westerflier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_TANGENT_TILT_OPTION_H #define KIS_TANGENT_TILT_OPTION_H #include #include #include const QString TANGENT_RED = "Tangent/swizzleRed"; const QString TANGENT_GREEN = "Tangent/swizzleGreen"; const QString TANGENT_BLUE = "Tangent/swizzleBlue"; const QString TANGENT_TYPE = "Tangent/directionType"; const QString TANGENT_EV_SEN = "Tangent/elevationSensitivity"; const QString TANGENT_MIX_VAL = "Tangent/mixValue"; //const QString TANGENT_DIR_MIN = "Tangent/directionMinimum"; //const QString TANGENT_DIR_MAX = "Tangent/directionMaximum"; class KisPropertiesConfiguration; -class KisPainter; class KisTangentTiltOptionWidget; class KisTangentTiltOption: public KisPaintOpOption//not really// { public: KisTangentTiltOption(); ~KisTangentTiltOption(); /*These three give away which the index of the combobox for a given channel*/ int redChannel() const; int greenChannel() const; int blueChannel() const; int directionType() const; double elevationSensitivity() const; double mixValue() const; qreal m_canvasAngle; bool m_canvasAxisXMirrored; bool m_canvasAxisYMirrored; /*This assigns the right axis to the component, based on index and maximum value*/ void swizzleAssign(qreal const horizontal, qreal const vertical, qreal const depth, quint8 *component, int index, qreal maxvalue); //takes the RGB values and will deform them depending on tilt. void apply(const KisPaintInformation& info,quint8 *r,quint8 *g,quint8 *b); void writeOptionSetting(KisPropertiesConfiguration* setting) const; void readOptionSetting(const KisPropertiesConfiguration* setting); private: KisTangentTiltOptionWidget * m_options; }; #endif // KIS_TANGENT_TILT_OPTION_H diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.cc b/krita/plugins/tools/defaulttools/kis_tool_brush.cc index 1c72ec81a1..be588f5838 100644 --- a/krita/plugins/tools/defaulttools/kis_tool_brush.cc +++ b/krita/plugins/tools/defaulttools/kis_tool_brush.cc @@ -1,441 +1,443 @@ /* * kis_tool_brush.cc - part of Krita * * Copyright (c) 2003-2004 Boudewijn Rempt * Copyright (c) 2015 Moritz Molch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_brush.h" #include #include #include #include +#include #include #include #include #include "kis_cursor.h" #include "kis_config.h" #include "kis_slider_spin_box.h" +#include "kundo2magicstring.h" #define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui #define MAXIMUM_MAGNETISM 1000 void KisToolBrush::addSmoothingAction(int enumId, const QString &id, const QString &name, KActionCollection *globalCollection) { /** * KisToolBrush is the base of several tools, but the actions * should be unique, so let's be careful with them */ if (!globalCollection->action(id)) { QAction *action = new QAction(name, globalCollection); globalCollection->addAction(id, action); } QAction *action = dynamic_cast(globalCollection->action(id)); addAction(id, action); connect(action, SIGNAL(triggered()), &m_signalMapper, SLOT(map())); m_signalMapper.setMapping(action, enumId); } KisToolBrush::KisToolBrush(KoCanvasBase * canvas) : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), kundo2_i18n("Freehand Brush Stroke")) { setObjectName("tool_brush"); connect(this, SIGNAL(smoothingTypeChanged()), this, SLOT(resetCursorStyle())); KActionCollection *collection = this->canvas()->canvasController()->actionCollection(); addSmoothingAction(KisSmoothingOptions::NO_SMOOTHING, "set_no_brush_smoothing", i18nc("@action", "Brush Smoothing: Disabled"), collection); addSmoothingAction(KisSmoothingOptions::SIMPLE_SMOOTHING, "set_simple_brush_smoothing", i18nc("@action", "Brush Smoothing: Basic"), collection); addSmoothingAction(KisSmoothingOptions::WEIGHTED_SMOOTHING, "set_weighted_brush_smoothing", i18nc("@action", "Brush Smoothing: Weighted"), collection); addSmoothingAction(KisSmoothingOptions::STABILIZER, "set_stabilizer_brush_smoothing", i18nc("@action", "Brush Smoothing: Stabilizer"), collection); } KisToolBrush::~KisToolBrush() { } void KisToolBrush::activate(ToolActivation activation, const QSet &shapes) { KisToolFreehand::activate(activation, shapes); connect(&m_signalMapper, SIGNAL(mapped(int)), SLOT(slotSetSmoothingType(int)), Qt::UniqueConnection); m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolBrush::deactivate() { disconnect(&m_signalMapper, 0, this, 0); KisToolFreehand::deactivate(); } int KisToolBrush::smoothingType() const { return smoothingOptions()->smoothingType(); } bool KisToolBrush::smoothPressure() const { return smoothingOptions()->smoothPressure(); } int KisToolBrush::smoothnessQuality() const { return smoothingOptions()->smoothnessDistance(); } qreal KisToolBrush::smoothnessFactor() const { return smoothingOptions()->tailAggressiveness(); } void KisToolBrush::slotSetSmoothingType(int index) { /** * The slot can also be called from smoothing-type-switching * action that would mean the combo box will not be synchronized */ if (m_cmbSmoothingType->currentIndex() != index) { m_cmbSmoothingType->setCurrentIndex(index); } switch (index) { case 0: smoothingOptions()->setSmoothingType(KisSmoothingOptions::NO_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 1: smoothingOptions()->setSmoothingType(KisSmoothingOptions::SIMPLE_SMOOTHING); showControl(m_sliderSmoothnessDistance, false); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, false); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 2: smoothingOptions()->setSmoothingType(KisSmoothingOptions::WEIGHTED_SMOOTHING); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, true); showControl(m_chkSmoothPressure, true); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, false); showControl(m_chkFinishStabilizedCurve, false); showControl(m_chkStabilizeSensors, false); break; case 3: default: smoothingOptions()->setSmoothingType(KisSmoothingOptions::STABILIZER); showControl(m_sliderSmoothnessDistance, true); showControl(m_sliderTailAggressiveness, false); showControl(m_chkSmoothPressure, false); showControl(m_chkUseScalableDistance, true); showControl(m_sliderDelayDistance, true); showControl(m_chkFinishStabilizedCurve, true); showControl(m_chkStabilizeSensors, true); } emit smoothingTypeChanged(); } void KisToolBrush::slotSetSmoothnessDistance(qreal distance) { smoothingOptions()->setSmoothnessDistance(distance); emit smoothnessQualityChanged(); } void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr) { smoothingOptions()->setTailAggressiveness(argh_rhhrr); emit smoothnessFactorChanged(); } // used with weighted smoothing void KisToolBrush::setSmoothPressure(bool value) { smoothingOptions()->setSmoothPressure(value); } void KisToolBrush::slotSetMagnetism(int magnetism) { m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0); } bool KisToolBrush::useScalableDistance() const { return smoothingOptions()->useScalableDistance(); } // used with weighted smoothing void KisToolBrush::setUseScalableDistance(bool value) { smoothingOptions()->setUseScalableDistance(value); emit useScalableDistanceChanged(); } void KisToolBrush::resetCursorStyle() { KisConfig cfg; CursorStyle cursorStyle = cfg.newCursorStyle(); // When the stabilizer is in use, we avoid using the brush outline cursor, // because it would hide the real position of the cursor to the user, // yielding unexpected results. if (smoothingOptions()->smoothingType() == KisSmoothingOptions::STABILIZER && smoothingOptions()->useDelayDistance() && cursorStyle == CURSOR_STYLE_NO_CURSOR) { useCursor(KisCursor::roundCursor()); } else { KisToolFreehand::resetCursorStyle(); } overrideCursorIfNotEditable(); } // stabilizer brush settings bool KisToolBrush::useDelayDistance() const { return smoothingOptions()->useDelayDistance(); } qreal KisToolBrush::delayDistance() const { return smoothingOptions()->delayDistance(); } void KisToolBrush::setUseDelayDistance(bool value) { smoothingOptions()->setUseDelayDistance(value); m_sliderDelayDistance->setEnabled(value); enableControl(m_chkFinishStabilizedCurve, !value); emit useDelayDistanceChanged(); } void KisToolBrush::setDelayDistance(qreal value) { smoothingOptions()->setDelayDistance(value); emit delayDistanceChanged(); } void KisToolBrush::setFinishStabilizedCurve(bool value) { smoothingOptions()->setFinishStabilizedCurve(value); emit finishStabilizedCurveChanged(); } bool KisToolBrush::finishStabilizedCurve() const { return smoothingOptions()->finishStabilizedCurve(); } void KisToolBrush::setStabilizeSensors(bool value) { smoothingOptions()->setStabilizeSensors(value); emit stabilizeSensorsChanged(); } bool KisToolBrush::stabilizeSensors() const { return smoothingOptions()->stabilizeSensors(); } void KisToolBrush::updateSettingsViews() { m_cmbSmoothingType->setCurrentIndex(smoothingOptions()->smoothingType()); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_cmbSmoothingType->setCurrentIndex((int)smoothingOptions()->smoothingType()); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); emit smoothnessQualityChanged(); emit smoothnessFactorChanged(); emit smoothPressureChanged(); emit smoothingTypeChanged(); emit useScalableDistanceChanged(); emit useDelayDistanceChanged(); emit delayDistanceChanged(); emit finishStabilizedCurveChanged(); emit stabilizeSensorsChanged(); KisTool::updateSettingsViews(); } QWidget * KisToolBrush::createOptionWidget() { QWidget *optionsWidget = KisToolFreehand::createOptionWidget(); optionsWidget->setObjectName(toolId() + "option widget"); // See https://bugs.kde.org/show_bug.cgi?id=316896 QWidget *specialSpacer = new QWidget(optionsWidget); specialSpacer->setObjectName("SpecialSpacer"); specialSpacer->setFixedSize(0, 0); optionsWidget->layout()->addWidget(specialSpacer); // Line smoothing configuration m_cmbSmoothingType = new QComboBox(optionsWidget); m_cmbSmoothingType->addItems(QStringList() << i18n("No Smoothing") << i18n("Basic Smoothing") << i18n("Weighted Smoothing") << i18n("Stabilizer")); connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int))); addOptionWidgetOption(m_cmbSmoothingType); m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1); m_sliderSmoothnessDistance->setEnabled(true); connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal))); m_sliderSmoothnessDistance->setValue(smoothingOptions()->smoothnessDistance()); addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Distance:"))); // Finish stabilizer curve m_chkFinishStabilizedCurve = new QCheckBox(optionsWidget); m_chkFinishStabilizedCurve->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkFinishStabilizedCurve->sizeHint().height())); connect(m_chkFinishStabilizedCurve, SIGNAL(toggled(bool)), this, SLOT(setFinishStabilizedCurve(bool))); m_chkFinishStabilizedCurve->setChecked(smoothingOptions()->finishStabilizedCurve()); // Delay Distance for Stabilizer QWidget* delayWidget = new QWidget(optionsWidget); QHBoxLayout* delayLayout = new QHBoxLayout(delayWidget); delayLayout->setContentsMargins(0,0,0,0); delayLayout->setSpacing(1); QLabel* delayLabel = new QLabel(i18n("Delay:"), optionsWidget); delayLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); delayLayout->addWidget(delayLabel); m_chkDelayDistance = new QCheckBox(optionsWidget); m_chkDelayDistance->setLayoutDirection(Qt::RightToLeft); delayWidget->setToolTip(i18n("Delay the brush stroke to make the line smoother")); connect(m_chkDelayDistance, SIGNAL(toggled(bool)), this, SLOT(setUseDelayDistance(bool))); delayLayout->addWidget(m_chkDelayDistance); m_sliderDelayDistance = new KisDoubleSliderSpinBox(optionsWidget); m_sliderDelayDistance->setToolTip(i18n("Radius where the brush is blocked")); m_sliderDelayDistance->setRange(0, 500); m_sliderDelayDistance->setSuffix(i18n(" px")); connect(m_sliderDelayDistance, SIGNAL(valueChanged(qreal)), SLOT(setDelayDistance(qreal))); addOptionWidgetOption(m_sliderDelayDistance, delayWidget); addOptionWidgetOption(m_chkFinishStabilizedCurve, new QLabel(i18n("Finish line:"))); m_sliderDelayDistance->setValue(smoothingOptions()->delayDistance()); m_chkDelayDistance->setChecked(smoothingOptions()->useDelayDistance()); // if the state is not flipped, then the previous line doesn't generate any signals setUseDelayDistance(m_chkDelayDistance->isChecked()); // Stabilize sensors m_chkStabilizeSensors = new QCheckBox(optionsWidget); m_chkStabilizeSensors->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkStabilizeSensors->sizeHint().height())); connect(m_chkStabilizeSensors, SIGNAL(toggled(bool)), this, SLOT(setStabilizeSensors(bool))); m_chkStabilizeSensors->setChecked(smoothingOptions()->stabilizeSensors()); addOptionWidgetOption(m_chkStabilizeSensors, new QLabel(i18n("Stabilize Sensors:"))); m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionsWidget); m_sliderTailAggressiveness->setRange(0.0, 1.0, 2); m_sliderTailAggressiveness->setEnabled(true); connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal))); m_sliderTailAggressiveness->setValue(smoothingOptions()->tailAggressiveness()); addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Stroke Ending:"))); m_chkSmoothPressure = new QCheckBox(optionsWidget); m_chkSmoothPressure->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkSmoothPressure->sizeHint().height())); m_chkSmoothPressure->setChecked(smoothingOptions()->smoothPressure()); connect(m_chkSmoothPressure, SIGNAL(toggled(bool)), this, SLOT(setSmoothPressure(bool))); addOptionWidgetOption(m_chkSmoothPressure, new QLabel(QString("%1:").arg(i18n("Smooth Pressure")))); m_chkUseScalableDistance = new QCheckBox(optionsWidget); m_chkUseScalableDistance->setChecked(smoothingOptions()->useScalableDistance()); m_chkUseScalableDistance->setMinimumHeight(qMax(m_sliderSmoothnessDistance->sizeHint().height()-3, m_chkUseScalableDistance->sizeHint().height())); m_chkUseScalableDistance->setToolTip(i18nc("@info:tooltip", "Scalable distance takes zoom level " "into account and makes the distance " "be visually constant whatever zoom " "level is chosen")); connect(m_chkUseScalableDistance, SIGNAL(toggled(bool)), this, SLOT(setUseScalableDistance(bool))); addOptionWidgetOption(m_chkUseScalableDistance, new QLabel(QString("%1:").arg(i18n("Scalable Distance")))); // Drawing assistant configuration QWidget* assistantWidget = new QWidget(optionsWidget); QHBoxLayout* assistantLayout = new QHBoxLayout(assistantWidget); assistantLayout->setContentsMargins(0,0,0,0); assistantLayout->setSpacing(1); QLabel* assistantLabel = new QLabel(i18n("Assistant:"), optionsWidget); assistantLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); assistantLayout->addWidget(assistantLabel); m_chkAssistant = new QCheckBox(optionsWidget); m_chkAssistant->setLayoutDirection(Qt::RightToLeft); assistantWidget->setToolTip(i18n("You need to add Ruler Assistants before this tool will work.")); connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool))); assistantLayout->addWidget(m_chkAssistant); m_sliderMagnetism = new KisSliderSpinBox(optionsWidget); m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism")); m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM); m_sliderMagnetism->setEnabled(false); connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, SLOT(setEnabled(bool))); m_sliderMagnetism->setValue(m_magnetism * MAXIMUM_MAGNETISM); connect(m_sliderMagnetism, SIGNAL(valueChanged(int)), SLOT(slotSetMagnetism(int))); QAction *toggleaction = new QAction(i18n("Toggle Assistant"), this); addAction("toggle_assistant", toggleaction); toggleaction->setShortcut(QKeySequence(Qt::ControlModifier + Qt::ShiftModifier + Qt::Key_L)); connect(toggleaction, SIGNAL(triggered(bool)), m_chkAssistant, SLOT(toggle())); addOptionWidgetOption(m_sliderMagnetism, assistantWidget); m_chkOnlyOneAssistant = new QCheckBox(optionsWidget); m_chkOnlyOneAssistant->setToolTip(i18nc("@info:tooltip","Make it only snap to a single assistant, prevents snapping mess while using the infinite assistants.")); m_chkOnlyOneAssistant->setCheckState(Qt::Checked);//turn on by default. connect(m_chkOnlyOneAssistant, SIGNAL(toggled(bool)), this, SLOT(setOnlyOneAssistantSnap(bool))); addOptionWidgetOption(m_chkOnlyOneAssistant, new QLabel(i18n("Snap single:"))); KisConfig cfg; slotSetSmoothingType(cfg.lineSmoothingType()); return optionsWidget; } diff --git a/krita/plugins/tools/defaulttools/kis_tool_multihand.cpp b/krita/plugins/tools/defaulttools/kis_tool_multihand.cpp index 69bcbd79b9..ce3810dcbd 100644 --- a/krita/plugins/tools/defaulttools/kis_tool_multihand.cpp +++ b/krita/plugins/tools/defaulttools/kis_tool_multihand.cpp @@ -1,412 +1,413 @@ /* * Copyright (c) 2011 Lukáš Tvrdý * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_multihand.h" #include #include #include #include #include #include +#include #include "kis_canvas2.h" #include "kis_cursor.h" #include "kis_tool_multihand_helper.h" static const int MAXIMUM_BRUSHES = 50; #include #ifdef Q_OS_WIN // quoting DRAND48(3) man-page: // These functions are declared obsolete by SVID 3, // which states that rand(3) should be used instead. #define drand48() (static_cast(qrand()) / static_cast(RAND_MAX)) #endif KisToolMultihand::KisToolMultihand(KoCanvasBase *canvas) : KisToolBrush(canvas), m_transformMode(SYMMETRY), m_angle(0), m_handsCount(6), m_mirrorVertically(false), m_mirrorHorizontally(false), m_showAxes(false), m_translateRadius(100), m_setupAxesFlag(false) { m_helper = new KisToolMultihandHelper(paintingInformationBuilder(), kundo2_i18n("Multibrush Stroke"), recordingAdapter()); resetHelper(m_helper); m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height()); } KisToolMultihand::~KisToolMultihand() { } void KisToolMultihand::beginPrimaryAction(KoPointerEvent *event) { if(m_setupAxesFlag) { setMode(KisTool::OTHER); m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } else { initTransformations(); KisToolFreehand::beginPrimaryAction(event); } } void KisToolMultihand::continuePrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { m_axesPoint = convertToPixelCoord(event->point); requestUpdateOutline(event->point, 0); updateCanvas(); } else { KisToolFreehand::continuePrimaryAction(event); } } void KisToolMultihand::endPrimaryAction(KoPointerEvent *event) { if(mode() == KisTool::OTHER) { setMode(KisTool::HOVER_MODE); requestUpdateOutline(event->point, 0); finishAxesSetup(); } else { KisToolFreehand::endPrimaryAction(event); } } void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter) { if(m_setupAxesFlag) { int diagonal = (currentImage()->height() + currentImage()->width()); QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); paintToolOutline(&gc, pixelToView(path)); } else { KisToolFreehand::paint(gc, converter); if(m_showAxes){ int diagonal = (currentImage()->height() + currentImage()->width()); QPainterPath path; path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle), m_axesPoint.y()-diagonal*sin(m_angle)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle), m_axesPoint.y()+diagonal*sin(m_angle)); path.moveTo(m_axesPoint.x()-diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()-diagonal*sin(m_angle+M_PI_2)); path.lineTo(m_axesPoint.x()+diagonal*cos(m_angle+M_PI_2), m_axesPoint.y()+diagonal*sin(m_angle+M_PI_2)); paintToolOutline(&gc, pixelToView(path)); } } } void KisToolMultihand::initTransformations() { QVector transformations; QTransform m; if(m_transformMode == SYMMETRY) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount; for(int i = 0; i < m_handsCount; i++) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep; } } else if(m_transformMode == MIRROR) { transformations << m; if (m_mirrorHorizontally) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically) { m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } if (m_mirrorVertically && m_mirrorHorizontally){ m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.scale(-1,-1); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } } else if(m_transformMode == SNOWFLAKE) { qreal angle = 0; qreal angleStep = (2 * M_PI) / m_handsCount/4; for(int i = 0; i < m_handsCount*4; i++) { if ((i%2)==1) { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.scale(-1,1); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } else { m.translate(m_axesPoint.x(), m_axesPoint.y()); m.rotateRadians(m_angle-angleStep); m.rotateRadians(angle); m.rotateRadians(-m_angle+angleStep); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); angle += angleStep*2; } } } else /* if(m_transformationNode == TRANSLATE) */ { /** * TODO: currently, the seed is the same for all the * strokes */ #ifdef Q_OS_WIN srand(0); #else srand48(0); #endif for (int i = 0; i < m_handsCount; i++){ qreal angle = drand48() * M_PI * 2; qreal length = drand48(); // convert the Polar coordinates to Cartesian coordinates qreal nx = (m_translateRadius * cos(angle) * length); qreal ny = (m_translateRadius * sin(angle) * length); m.translate(m_axesPoint.x(),m_axesPoint.y()); m.rotateRadians(m_angle); m.translate(nx,ny); m.rotateRadians(-m_angle); m.translate(-m_axesPoint.x(), -m_axesPoint.y()); transformations << m; m.reset(); } } m_helper->setupTransformations(transformations); } QWidget* KisToolMultihand::createOptionWidget() { QWidget *widget = KisToolBrush::createOptionWidget(); m_axesChCkBox = new QCheckBox(i18n("Show Axes")); connect(m_axesChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetAxesVisible(bool))); m_axesPointBtn = new QPushButton(i18n("Axes point"), widget); m_axesPointBtn->setCheckable(true); connect(m_axesPointBtn, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup())); addOptionWidgetOption(m_axesPointBtn, m_axesChCkBox); m_axesAngleSlider = new KisDoubleSliderSpinBox(widget); m_axesAngleSlider->setToolTip(i18n("Set axes angle (degrees)")); m_axesAngleSlider->setSuffix(QChar(Qt::Key_degree)); m_axesAngleSlider->setRange(0.0, 90.0,1); m_axesAngleSlider->setEnabled(true); connect(m_axesAngleSlider, SIGNAL(valueChanged(qreal)),this, SLOT(slotSetAxesAngle(qreal))); addOptionWidgetOption(m_axesAngleSlider, new QLabel(i18n("Axes Angle:"))); m_transformModesComboBox = new QComboBox(widget); m_transformModesComboBox->addItem(i18n("Symmetry"),int(SYMMETRY)); m_transformModesComboBox->addItem(i18n("Mirror"),int(MIRROR)); m_transformModesComboBox->addItem(i18n("Translate"),int(TRANSLATE)); m_transformModesComboBox->addItem(i18n("Snowflake"),int(SNOWFLAKE)); connect(m_transformModesComboBox,SIGNAL(currentIndexChanged(int)),SLOT(slotSetTransformMode(int))); addOptionWidgetOption(m_transformModesComboBox); m_handsCountSlider = new KisSliderSpinBox(widget); m_handsCountSlider->setToolTip(i18n("Brush count")); m_handsCountSlider->setRange(1, MAXIMUM_BRUSHES); m_handsCountSlider->setEnabled(true); connect(m_handsCountSlider, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int))); addOptionWidgetOption(m_handsCountSlider); m_modeCustomOption = new QStackedWidget(widget); QWidget * symmetryWidget = new QWidget(m_modeCustomOption); m_modeCustomOption->addWidget(symmetryWidget); QWidget * mirrorWidget = new QWidget(m_modeCustomOption); m_mirrorHorizontallyChCkBox = new QCheckBox(i18n("Horizontally")); m_mirrorVerticallyChCkBox = new QCheckBox(i18n("Vertically")); connect(m_mirrorHorizontallyChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetMirrorHorizontally(bool))); connect(m_mirrorVerticallyChCkBox,SIGNAL(toggled(bool)),this, SLOT(slotSetMirrorVertically(bool))); QGridLayout * mirrorLayout = new QGridLayout(mirrorWidget); mirrorLayout->addWidget(m_mirrorHorizontallyChCkBox,0,0); mirrorLayout->addWidget(m_mirrorVerticallyChCkBox,0,1); mirrorWidget->setLayout(mirrorLayout); m_modeCustomOption->addWidget(mirrorWidget); QWidget * translateWidget = new QWidget(m_modeCustomOption); m_translateRadiusSlider = new KisSliderSpinBox(translateWidget); m_translateRadiusSlider->setRange(0, 200); m_translateRadiusSlider->setSuffix(" px"); connect(m_translateRadiusSlider,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int))); QFormLayout *radiusLayout = new QFormLayout(translateWidget); radiusLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); radiusLayout->addRow(i18n("Radius"), m_translateRadiusSlider); translateWidget->setLayout(radiusLayout); m_modeCustomOption->addWidget(translateWidget); m_modeCustomOption->setCurrentIndex(m_transformModesComboBox->currentIndex()); addOptionWidgetOption(m_modeCustomOption); // read values from configuration file m_axesChCkBox->setChecked((bool)m_configGroup.readEntry("showAxes", false)); m_mirrorHorizontallyChCkBox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false)); m_mirrorVerticallyChCkBox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false)); m_axesAngleSlider->setValue(m_configGroup.readEntry("axesAngle", 0.0)); m_transformModesComboBox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0)); m_translateRadiusSlider->setValue(m_configGroup.readEntry("translateRadius", 0)); m_handsCountSlider->setValue(m_configGroup.readEntry("handsCount", 4)); return widget; } void KisToolMultihand::activateAxesPointModeSetup() { if (m_axesPointBtn->isChecked()){ m_setupAxesFlag = true; useCursor(KisCursor::crossCursor()); updateCanvas(); } else { finishAxesSetup(); } } void KisToolMultihand::finishAxesSetup() { m_setupAxesFlag = false; m_axesPointBtn->setChecked(false); resetCursorStyle(); updateCanvas(); } void KisToolMultihand::updateCanvas() { KisCanvas2 *kisCanvas = dynamic_cast(canvas()); Q_ASSERT(kisCanvas); kisCanvas->updateCanvas(); } void KisToolMultihand::slotSetHandsCount(int count) { m_handsCount = count; m_configGroup.writeEntry("handsCount", count); } void KisToolMultihand::slotSetAxesAngle(qreal angle) { //negative so axes rotates counter clockwise m_angle = -angle*M_PI/180; updateCanvas(); m_configGroup.writeEntry("axesAngle", angle); } void KisToolMultihand::slotSetTransformMode(int index) { m_transformMode = enumTransforModes(m_transformModesComboBox->itemData(index).toInt()); m_modeCustomOption->setCurrentIndex(index); m_handsCountSlider->setVisible(m_transformMode != MIRROR); m_configGroup.writeEntry("transformMode", index); } void KisToolMultihand::slotSetAxesVisible(bool vis) { m_showAxes = vis; updateCanvas(); m_configGroup.writeEntry("showAxes", vis); } void KisToolMultihand::slotSetMirrorVertically(bool mirror) { m_mirrorVertically = mirror; m_configGroup.writeEntry("mirrorVertically", mirror); } void KisToolMultihand::slotSetMirrorHorizontally(bool mirror) { m_mirrorHorizontally = mirror; m_configGroup.writeEntry("mirrorHorizontally", mirror); } void KisToolMultihand::slotSetTranslateRadius(int radius) { m_translateRadius = radius; m_configGroup.writeEntry("translateRadius", radius); } diff --git a/krita/plugins/tools/tool_dyna/kis_tool_dyna.cpp b/krita/plugins/tools/tool_dyna/kis_tool_dyna.cpp index 07eb6e3f18..691a49d04c 100644 --- a/krita/plugins/tools/tool_dyna/kis_tool_dyna.cpp +++ b/krita/plugins/tools/tool_dyna/kis_tool_dyna.cpp @@ -1,352 +1,354 @@ /* * kis_tool_dyna.cpp - part of Krita * * Copyright (c) 2009-2011 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_dyna.h" #include #include +#include #include #include #include "KoPointerEvent.h" +#include "kundo2magicstring.h" #include "kis_cursor.h" #include #define MAXIMUM_SMOOTHNESS 1000 #define MAXIMUM_MAGNETISM 1000 #define MIN_MASS 1.0 #define MAX_MASS 160.0 #define MIN_DRAG 0.0 #define MAX_DRAG 0.5 #define MIN_ACC 0.000001 #define MIN_VEL 0.000001 KisToolDyna::KisToolDyna(KoCanvasBase * canvas) : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), kundo2_i18n("Dynamic Brush Stroke")) { setObjectName("tool_dyna"); initDyna(); } void KisToolDyna::initDyna() { /* dynadraw init */ m_curmass = 0.5; m_curdrag = 0.15; m_mouse.fixedangle = false; m_width = 1.5; m_xangle = 0.60; m_yangle = 0.20; m_widthRange = 0.05; } KisToolDyna::~KisToolDyna() { } void KisToolDyna::resetCursorStyle() { KisToolFreehand::resetCursorStyle(); overrideCursorIfNotEditable(); } void KisToolDyna::activate(ToolActivation toolActivation, const QSet &shapes) { KisToolPaint::activate(toolActivation, shapes); m_configGroup = KSharedConfig::openConfig()->group(toolId()); } void KisToolDyna::initStroke(KoPointerEvent *event) { QRectF imageSize = QRectF(QPointF(0.0,0.0),currentImage()->size()); QRectF documentSize = currentImage()->pixelToDocument(imageSize); m_surfaceWidth = documentSize.width(); m_surfaceHeight = documentSize.height(); setMousePosition(event->point); m_mouse.init(m_mousePos.x(), m_mousePos.y()); KisToolFreehand::initStroke(event); } void KisToolDyna::beginPrimaryAction(KoPointerEvent *event) { setMousePosition(event->point); m_mouse.init(m_mousePos.x(), m_mousePos.y()); m_odelx = m_mousePos.x(); m_odely = m_mousePos.y(); KisToolFreehand::beginPrimaryAction(event); } void KisToolDyna::continuePrimaryAction(KoPointerEvent *event) { setMousePosition(event->point); if (applyFilter(m_mousePos.x(), m_mousePos.y())) { KoPointerEvent newEvent = filterEvent(event); KisToolFreehand::continuePrimaryAction(&newEvent); } } // dyna algorithm int KisToolDyna::applyFilter(qreal mx, qreal my) { /* calculate mass and drag */ qreal mass = flerp(MIN_MASS, MAX_MASS, m_curmass); qreal drag = flerp(MIN_DRAG, MAX_DRAG, m_curdrag * m_curdrag); /* calculate force and acceleration */ qreal fx = mx - m_mouse.curx; qreal fy = my - m_mouse.cury; m_mouse.acc = sqrt(fx * fx + fy * fy); if (m_mouse.acc < MIN_ACC) { return 0; } m_mouse.accx = fx / mass; m_mouse.accy = fy / mass; /* calculate new velocity */ m_mouse.velx += m_mouse.accx; m_mouse.vely += m_mouse.accy; m_mouse.vel = sqrt(m_mouse.velx * m_mouse.velx + m_mouse.vely * m_mouse.vely); m_mouse.angx = -m_mouse.vely; m_mouse.angy = m_mouse.velx; if (m_mouse.vel < MIN_VEL) { return 0; } /* calculate angle of drawing tool */ if (m_mouse.fixedangle) { m_mouse.angx = m_xangle; m_mouse.angy = m_yangle; } else { m_mouse.angx /= m_mouse.vel; m_mouse.angy /= m_mouse.vel; } m_mouse.velx = m_mouse.velx * (1.0 - drag); m_mouse.vely = m_mouse.vely * (1.0 - drag); m_mouse.lastx = m_mouse.curx; m_mouse.lasty = m_mouse.cury; m_mouse.curx = m_mouse.curx + m_mouse.velx; m_mouse.cury = m_mouse.cury + m_mouse.vely; return 1; } KoPointerEvent KisToolDyna::filterEvent(KoPointerEvent* event) { qreal wid = m_widthRange - m_mouse.vel; wid = wid * m_width; if (wid < 0.00001) { wid = 0.00001; } qreal delx = m_mouse.angx * wid; qreal dely = m_mouse.angy * wid; qreal px = m_mouse.lastx; qreal py = m_mouse.lasty; qreal nx = m_mouse.curx; qreal ny = m_mouse.cury; QPointF prev(px , py); // previous position QPointF now(nx , ny); // new position QPointF prevr(px + m_odelx , py + m_odely); QPointF prevl(px - m_odelx , py - m_odely); QPointF nowl(nx - delx , ny - dely); QPointF nowr(nx + delx , ny + dely); // transform coords from float points into image points prev.rx() *= m_surfaceWidth; prevr.rx() *= m_surfaceWidth; prevl.rx() *= m_surfaceWidth; now.rx() *= m_surfaceWidth; nowl.rx() *= m_surfaceWidth; nowr.rx() *= m_surfaceWidth; prev.ry() *= m_surfaceHeight; prevr.ry() *= m_surfaceHeight; prevl.ry() *= m_surfaceHeight; now.ry() *= m_surfaceHeight; nowl.ry() *= m_surfaceHeight; nowr.ry() *= m_surfaceHeight; qreal m_pressure; #if 0 qreal xTilt, yTilt; qreal m_rotation; qreal m_tangentialPressure; // some funny debugging dbgPlugins << "m_mouse.vel: " << m_mouse.vel; dbgPlugins << "m_mouse.velx: " << m_mouse.velx; dbgPlugins << "m_mouse.vely: " << m_mouse.vely; dbgPlugins << "m_mouse.accx: " << m_mouse.accx; dbgPlugins << "m_mouse.accy: " << m_mouse.accy; dbgPlugins << "fixed: " << m_mouse.fixedangle; dbgPlugins << "drag: " << m_curdrag; dbgPlugins << "mass: " << m_curmass; dbgPlugins << "xAngle: " << m_xangle; dbgPlugins << "yAngle: " << m_yangle; #endif m_pressure = m_mouse.vel * 100; m_pressure = qBound(0.0,m_pressure, 1.0); m_odelx = delx; m_odely = dely; // how to change pressure in the KoPointerEvent??? return KoPointerEvent(event,now); } void KisToolDyna::slotSetDrag(qreal drag) { m_curdrag = drag; m_configGroup.writeEntry("dragAmount", drag); } void KisToolDyna::slotSetMass(qreal mass) { m_curmass = mass; m_configGroup.writeEntry("massAmount", mass); } void KisToolDyna::slotSetDynaWidth(double width) { m_width = width; m_configGroup.writeEntry("initWidth", width); } void KisToolDyna::slotSetWidthRange(double widthRange) { m_widthRange = widthRange; m_configGroup.writeEntry("initWidthRange", widthRange); } void KisToolDyna::slotSetFixedAngle(bool fixedAngle) { m_mouse.fixedangle = fixedAngle; m_angleDSSBox->setEnabled(fixedAngle); m_configGroup.writeEntry("useFixedAngle", fixedAngle); } QWidget * KisToolDyna::createOptionWidget() { QWidget * optionsWidget = KisToolFreehand::createOptionWidget(); optionsWidget->setObjectName(toolId() + "option widget"); m_optionLayout = new QGridLayout(optionsWidget); Q_CHECK_PTR(m_optionLayout); m_optionLayout->setMargin(0); m_optionLayout->setSpacing(2); KisToolFreehand::addOptionWidgetLayout(m_optionLayout); QLabel* massLbl = new QLabel(i18n("Mass:"), optionsWidget); m_massSPBox = new KisDoubleSliderSpinBox(optionsWidget); m_massSPBox->setRange(0.0,1.0,2); connect(m_massSPBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetMass(qreal))); KisToolFreehand::addOptionWidgetOption(m_massSPBox,massLbl); QLabel* dragLbl = new QLabel(i18n("Drag:"), optionsWidget); m_dragSPBox = new KisDoubleSliderSpinBox(optionsWidget); m_dragSPBox->setRange(0.0,1.0,2); connect(m_dragSPBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetDrag(qreal))); KisToolFreehand::addOptionWidgetOption(m_dragSPBox,dragLbl); //NOTE: so far unused, waiting for the changes to propagate rotation/pressure to freehand tool // fixed angle might be for 2.4, but the later one for 2.5 m_chkFixedAngle = new QCheckBox(i18n("Fixed angle:"), optionsWidget); m_chkFixedAngle->setEnabled(false); connect(m_chkFixedAngle, SIGNAL(toggled(bool)), this, SLOT(slotSetFixedAngle(bool))); m_angleDSSBox = new KisDoubleSliderSpinBox(optionsWidget); m_angleDSSBox->setRange(0,360,0); m_angleDSSBox->setSuffix(QChar(Qt::Key_degree)); m_angleDSSBox->setEnabled(false); connect(m_angleDSSBox, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAngle(qreal))); KisToolFreehand::addOptionWidgetOption(m_angleDSSBox,m_chkFixedAngle); // read settings in from config m_massSPBox->setValue(m_configGroup.readEntry("massAmount", 0.01)); m_dragSPBox->setValue(m_configGroup.readEntry("dragAmount", .98)); m_chkFixedAngle->setChecked((bool)m_configGroup.readEntry("useFixedAngle", false)); m_angleDSSBox->setValue(m_configGroup.readEntry("angleAmount", 20)); #if 0 QLabel* initWidthLbl = new QLabel(i18n("Initial width:"), optionWidget); m_initWidthSPBox = new QDoubleSpinBox(optionWidget); connect(m_initWidthSPBox, SIGNAL(valueChanged(double)), this, SLOT(slotSetDynaWidth(double))); KisToolFreehand::addOptionWidgetOption(m_initWidthSPBox,initWidthLbl); QLabel* widthRangeLbl = new QLabel(i18n("Width range:"), optionWidget); m_widthRangeSPBox = new QDoubleSpinBox(optionWidget); connect(m_widthRangeSPBox, SIGNAL(valueChanged(double)), this, SLOT(slotSetWidthRange(double))); //KisToolFreehand::addOptionWidgetOption(m_widthRangeSPBox,widthRangeLbl); m_initWidthSPBox->setValue(m_configGroup.readEntry("initWidth", 10)); m_widthRangeSPBox->setValue(m_configGroup.readEntry("initWidthRange", 20)); #endif return optionsWidget; } void KisToolDyna::slotSetAngle(qreal angle) { m_xangle = cos(angle * M_PI/180.0); m_yangle = sin(angle * M_PI/180.0); m_configGroup.writeEntry("angleAmount", angle); } diff --git a/krita/plugins/tools/tool_grid/kis_tool_grid.cc b/krita/plugins/tools/tool_grid/kis_tool_grid.cc index cb10bfebf3..1e604ac0ca 100644 --- a/krita/plugins/tools/tool_grid/kis_tool_grid.cc +++ b/krita/plugins/tools/tool_grid/kis_tool_grid.cc @@ -1,176 +1,177 @@ /* * Copyright (c) 2008 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_grid.h" #include #include #include #include #include #include #include #include #include #include #include +#include "kis_canvas_decoration.h" KisToolGrid::KisToolGrid(KoCanvasBase * canvas) : KisTool(canvas, KisCursor::moveCursor()), m_canvas(dynamic_cast(canvas)) { Q_ASSERT(m_canvas); setObjectName("tool_grid"); } KisToolGrid::~KisToolGrid() { } void KisToolGrid::activate(ToolActivation toolActivation, const QSet &shapes) { KisTool::activate(toolActivation, shapes); m_canvas->updateCanvas(); KisCanvasDecoration* decoration = m_canvas->decoration("grid"); if (decoration && !decoration->visible()) { m_canvas->viewManager()->showFloatingMessage(i18n("The grid is not visible. Press Return to show the grid."), KisIconUtils::loadIcon("krita_tool_grid")); } } inline QPointF modPoints(const QPointF &x1, const QPointF &x2) { return QPointF(std::fmod(x1.x(), x2.x()), std::fmod(x1.y(), x2.y())); } inline QPointF divPoints(const QPointF &x1, const QPointF &x2) { return QPointF(x1.x() / x2.x(), x1.y() / x2.y()); } inline QPointF mulPoints(const QPointF &x1, const QPointF &x2) { return QPointF(x1.x() * x2.x(), x1.y() * x2.y()); } void KisToolGrid::beginPrimaryAction(KoPointerEvent *event) { setMode(KisTool::PAINT_MODE); m_dragStart = convertToPixelCoord(event); KisConfig cfg; m_initialSpacing = QPoint(cfg.getGridHSpacing(), cfg.getGridVSpacing()); m_initialOffset = QPoint(cfg.getGridOffsetX(), cfg.getGridOffsetY()); } void KisToolGrid::continuePrimaryAction(KoPointerEvent *event) { KisConfig cfg; m_dragEnd = convertToPixelCoord(event); QPointF newOffset = m_initialOffset + m_dragEnd - m_dragStart; newOffset = modPoints(newOffset, qreal(cfg.getGridSubdivisions()) * m_initialSpacing); cfg.setGridOffsetX(newOffset.x()); cfg.setGridOffsetY(newOffset.y()); m_canvas->updateCanvas(); } void KisToolGrid::endPrimaryAction(KoPointerEvent *event) { Q_UNUSED(event); setMode(KisTool::HOVER_MODE); } void KisToolGrid::beginAlternateAction(KoPointerEvent *event, AlternateAction action) { if (action != Secondary && action != PickFgNode && action != PickFgImage) { KisTool::beginAlternateAction(event, action); return; } KisConfig cfg; m_initialSpacing = QPoint(cfg.getGridHSpacing(), cfg.getGridVSpacing()); m_initialOffset = QPoint(cfg.getGridOffsetX(), cfg.getGridOffsetY()); m_dragStart = convertToPixelCoord(event); } void KisToolGrid::continueAlternateAction(KoPointerEvent *event, AlternateAction action) { if (action != Secondary && action != PickFgNode && action != PickFgImage) { KisTool::continueAlternateAction(event, action); return; } KisConfig cfg; m_dragEnd = convertToPixelCoord(event); QPoint newSpacing = m_initialSpacing + (m_dragEnd - m_dragStart).toPoint(); QPointF newOffset = m_dragStart - divPoints(mulPoints(modPoints(m_dragStart - m_initialOffset, m_initialSpacing), newSpacing), m_initialSpacing); newOffset = modPoints(newOffset, qreal(cfg.getGridSubdivisions()) * newSpacing); if (newSpacing.x() >= 1 && newSpacing.y() >= 1) { cfg.setGridHSpacing(newSpacing.x()); cfg.setGridVSpacing(newSpacing.y()); cfg.setGridOffsetX(newOffset.x()); cfg.setGridOffsetY(newOffset.y()); } m_canvas->updateCanvas(); } void KisToolGrid::endAlternateAction(KoPointerEvent *event, AlternateAction action) { if (action != Secondary && action != PickFgNode && action != PickFgImage) { KisTool::endAlternateAction(event, action); return; } setMode(KisTool::HOVER_MODE); } void KisToolGrid::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(gc); Q_UNUSED(converter); } void KisToolGrid::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Return) { KisCanvasDecoration* decoration = m_canvas->decoration("grid"); if (decoration) { decoration->setVisible(true); } m_canvas->viewManager()->gridManager()->checkVisibilityAction(true); } KoToolBase::keyPressEvent(event); } diff --git a/krita/ui/KisApplication.h b/krita/ui/KisApplication.h index 489b69ce3d..cfb42d5646 100644 --- a/krita/ui/KisApplication.h +++ b/krita/ui/KisApplication.h @@ -1,111 +1,108 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_APPLICATION_H #define KIS_APPLICATION_H #include #include "kritaui_export.h" class KisMainWindow; class KisApplicationPrivate; -class KCmdLineArgs; class QWidget; -class QStringList; -class QCommandLineParser; class KisApplicationArguments; #include /** * @brief Base class for all %Calligra apps * * This class handles arguments given on the command line and * shows a generic about dialog for all Calligra apps. * * In addition it adds the standard directories where Calligra applications * can find their images etc. * * If the last mainwindow becomes closed, KisApplication automatically * calls QApplication::quit. */ class KRITAUI_EXPORT KisApplication : public QtSingleApplication { Q_OBJECT public: /** * Creates an application object, adds some standard directories and * initializes kimgio. */ explicit KisApplication(const QString &key, int &argc, char **argv); /** * Destructor. */ virtual ~KisApplication(); /** * Call this to start the application. * * Parses command line arguments and creates the initial main windowss and docs * from them (or an empty doc if no cmd-line argument is specified ). * * You must call this method directly before calling QApplication::exec. * * It is valid behaviour not to call this method at all. In this case you * have to process your command line parameters by yourself. */ virtual bool start(const KisApplicationArguments &args); /** * Checks if user is holding ctrl+alt+shift keys and asks if the settings file should be cleared. * * Typically called during startup before reading the config. */ void askClearConfig(); /** * Tell KisApplication to show this splashscreen when you call start(); * when start returns, the splashscreen is hidden. Use KSplashScreen * to have the splash show correctly on Xinerama displays. */ void setSplashScreen(QWidget *splash); /// Overridden to handle exceptions from event handlers. bool notify(QObject *receiver, QEvent *event); public Q_SLOTS: void remoteArguments(QByteArray message, QObject*socket); void fileOpenRequested(const QString & url); private: /// @return the number of autosavefiles opened QList checkAutosaveFiles(); bool createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow); void clearConfig(); private: KisApplicationPrivate * const d; class ResetStarting; friend class ResetStarting; }; #endif diff --git a/krita/ui/KisDocument.cpp b/krita/ui/KisDocument.cpp index 477b30ef2c..89cfb96be2 100644 --- a/krita/ui/KisDocument.cpp +++ b/krita/ui/KisDocument.cpp @@ -1,2640 +1,2639 @@ /* This file is part of the Krita project * * Copyright (C) 2014 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // XXX: remove #include // XXX: remove #include "KisApplication.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPart.h" #include "KisView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "kundo2stack.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Krita Image #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include "KisViewManager.h" #include "kis_clipboard.h" #include "widgets/kis_custom_image_widget.h" #include "canvas/kis_canvas2.h" #include "flake/kis_shape_controller.h" #include "kra/kis_kra_loader.h" #include "kra/kis_kra_saver.h" #include "kis_statusbar.h" #include "widgets/kis_progress_widget.h" #include "kis_canvas_resource_provider.h" #include "kis_resource_server_provider.h" #include "kis_node_manager.h" #include "KisPart.h" static const char CURRENT_DTD_VERSION[] = "2.0"; // Define the protocol used here for embedded documents' URL // This used to "store" but QUrl didn't like it, // so let's simply make it "tar" ! #define STORE_PROTOCOL "tar" // The internal path is a hack to make QUrl happy and for document children #define INTERNAL_PROTOCOL "intern" #define INTERNAL_PREFIX "intern:/" // Warning, keep it sync in koStore.cc #include using namespace std; /********************************************************** * * KisDocument * **********************************************************/ namespace { class DocumentProgressProxy : public KoProgressProxy { public: KisMainWindow *m_mainWindow; DocumentProgressProxy(KisMainWindow *mainWindow) : m_mainWindow(mainWindow) { } ~DocumentProgressProxy() { // signal that the job is done setValue(-1); } int maximum() const { return 100; } void setValue(int value) { if (m_mainWindow) { m_mainWindow->slotProgress(value); } } void setRange(int /*minimum*/, int /*maximum*/) { } void setFormat(const QString &/*format*/) { } }; } //static QString KisDocument::newObjectName() { static int s_docIFNumber = 0; QString name; name.setNum(s_docIFNumber++); name.prepend("document_"); return name; } class UndoStack : public KUndo2Stack { public: UndoStack(KisDocument *doc) : m_doc(doc) { } void setIndex(int idx) { KisImageWSP image = this->image(); image->requestStrokeCancellation(); if(image->tryBarrierLock()) { KUndo2Stack::setIndex(idx); image->unlock(); } } void notifySetIndexChangedOneCommand() { KisImageWSP image = this->image(); image->unlock(); image->barrierLock(); } void undo() { KisImageWSP image = this->image(); image->requestUndoDuringStroke(); if(image->tryBarrierLock()) { KUndo2Stack::undo(); image->unlock(); } } void redo() { KisImageWSP image = this->image(); if(image->tryBarrierLock()) { KUndo2Stack::redo(); image->unlock(); } } private: KisImageWSP image() { KisImageWSP currentImage = m_doc->image(); Q_ASSERT(currentImage); return currentImage; } private: KisDocument *m_doc; }; class Q_DECL_HIDDEN KisDocument::Private { public: Private(KisDocument *document) : document(document), // XXX: the part should _not_ be modified from the document docInfo(0), progressUpdater(0), progressProxy(0), profileStream(0), filterManager(0), specialOutputFlag(0), // default is native format isImporting(false), isExporting(false), password(QString()), modifiedAfterAutosave(false), autosaving(false), shouldCheckAutoSaveFile(true), autoErrorHandlingEnabled(true), backupFile(true), backupPath(QString()), doNotSaveExtDoc(false), storeInternal(false), isLoading(false), undoStack(0), m_job(0), m_statJob(0), m_uploadJob(0), m_saveOk(false), m_waitForSave(false), m_duringSaveAs(false), m_bTemp(false), m_bAutoDetectedMime(false), modified(false), readwrite(true), disregardAutosaveFailure(false), nserver(0), macroNestDepth(0), kraLoader(0) { if (QLocale().measurementSystem() == QLocale::ImperialSystem) { unit = KoUnit::Inch; } else { unit = KoUnit::Centimeter; } } ~Private() { // Don't delete m_d->shapeController because it's in a QObject hierarchy. delete nserver; } KisDocument *document; KoDocumentInfo *docInfo; KoProgressUpdater *progressUpdater; KoProgressProxy *progressProxy; QTextStream *profileStream; QTime profileReferenceTime; KoUnit unit; KisImportExportManager *filterManager; // The filter-manager to use when loading/saving [for the options] QByteArray mimeType; // The actual mimetype of the document QByteArray outputMimeType; // The mimetype to use when saving bool confirmNonNativeSave [2] = {true, true}; // used to pop up a dialog when saving for the // first time if the file is in a foreign format // (Save/Save As, Export) int specialOutputFlag; // See KoFileDialog in koMainWindow.cc bool isImporting; bool isExporting; // File --> Import/Export vs File --> Open/Save QString password; // The password used to encrypt an encrypted document QTimer autoSaveTimer; QString lastErrorMessage; // see openFile() int autoSaveDelay; // in seconds, 0 to disable. bool modifiedAfterAutosave; bool autosaving; bool shouldCheckAutoSaveFile; // usually true bool autoErrorHandlingEnabled; // usually true bool backupFile; QString backupPath; bool doNotSaveExtDoc; // makes it possible to save only internally stored child documents bool storeInternal; // Store this doc internally even if url is external bool isLoading; // True while loading (openUrl is async) KUndo2Stack *undoStack; KoGridData gridData; KoGuidesData guidesData; bool isEmpty; KoPageLayout pageLayout; KIO::FileCopyJob * m_job; KIO::StatJob * m_statJob; KIO::FileCopyJob * m_uploadJob; QUrl m_originalURL; // for saveAs QString m_originalFilePath; // for saveAs bool m_saveOk : 1; bool m_waitForSave : 1; bool m_duringSaveAs : 1; bool m_bTemp: 1; // If @p true, @p m_file is a temporary file that needs to be deleted later. bool m_bAutoDetectedMime : 1; // whether the mimetype in the arguments was detected by the part itself QUrl m_url; // Remote (or local) url - the one displayed to the user. QString m_file; // Local file - the only one the part implementation should deal with. QEventLoop m_eventLoop; bool modified; bool readwrite; bool disregardAutosaveFailure; KisNameServer *nserver; qint32 macroNestDepth; KisImageSP image; KisNodeSP preActivatedNode; KisShapeController* shapeController; KoShapeController* koShapeController; KisKraLoader* kraLoader; KisKraSaver* kraSaver; QList assistants; bool openFile() { DocumentProgressProxy *progressProxy = 0; if (!document->progressProxy()) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainWindows().count() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } progressProxy = new DocumentProgressProxy(mainWindow); document->setProgressProxy(progressProxy); } document->setUrl(m_url); bool ok = document->openFile(); if (progressProxy) { document->setProgressProxy(0); delete progressProxy; } return ok; } bool openLocalFile() { m_bTemp = false; // set the mimetype only if it was not already set (for example, by the host application) if (mimeType.isEmpty()) { // get the mimetype of the file // using findByUrl() to avoid another string -> url conversion QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(m_url.toLocalFile()); if (mime.isValid()) { mimeType = mime.name().toLocal8Bit(); m_bAutoDetectedMime = true; } } const bool ret = openFile(); if (ret) { emit document->completed(); } else { emit document->canceled(QString()); } return ret; } void openRemoteFile() { m_bTemp = true; // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) QString fileName = m_url.fileName(); QFileInfo fileInfo(fileName); QString ext = fileInfo.completeSuffix(); QString extension; if (!ext.isEmpty() && m_url.query().isNull()) // not if the URL has a query, e.g. cgi.pl?something extension = '.'+ext; // keep the '.' QTemporaryFile tempFile(QDir::tempPath() + QLatin1String("/krita_XXXXXX") + extension); tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); QUrl destURL; destURL.setPath( m_file ); KIO::JobFlags flags = KIO::DefaultFlags; flags |= KIO::Overwrite; m_job = KIO::file_copy(m_url, destURL, 0600, flags); QObject::connect(m_job, SIGNAL(result(KJob*)), document, SLOT(_k_slotJobFinished(KJob*))); QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*,QString)), document, SLOT(_k_slotGotMimeType(KIO::Job*,QString))); } // Set m_file correctly for m_url void prepareSaving() { // Local file if ( m_url.isLocalFile() ) { if ( m_bTemp ) // get rid of a possible temp file first { // (happens if previous url was remote) QFile::remove( m_file ); m_bTemp = false; } m_file = m_url.toLocalFile(); } else { // Remote file // We haven't saved yet, or we did but locally - provide a temp file if ( m_file.isEmpty() || !m_bTemp ) { QTemporaryFile tempFile; tempFile.setAutoRemove(false); tempFile.open(); m_file = tempFile.fileName(); m_bTemp = true; } // otherwise, we already had a temp file } } void _k_slotJobFinished( KJob * job ) { Q_ASSERT( job == m_job ); m_job = 0; if (job->error()) emit document->canceled( job->errorString() ); else { if ( openFile() ) { emit document->completed(); } else { emit document->canceled(QString()); } } } void _k_slotStatJobFinished(KJob * job) { Q_ASSERT(job == m_statJob); m_statJob = 0; // this could maybe confuse some apps? So for now we'll just fallback to KIO::get // and error again. Well, maybe this even helps with wrong stat results. if (!job->error()) { const QUrl localUrl = static_cast(job)->url(); if (localUrl.isLocalFile()) { m_file = localUrl.toLocalFile(); openLocalFile(); return; } } openRemoteFile(); } void _k_slotGotMimeType(KIO::Job *job, const QString &mime) { dbgFile << mime; Q_ASSERT(job == m_job); Q_UNUSED(job); // set the mimetype only if it was not already set (for example, by the host application) if (mimeType.isEmpty()) { mimeType = mime.toLocal8Bit(); m_bAutoDetectedMime = true; } } void _k_slotUploadFinished( KJob * ) { if (m_uploadJob->error()) { QFile::remove(m_uploadJob->srcUrl().toLocalFile()); m_uploadJob = 0; if (m_duringSaveAs) { document->setUrl(m_originalURL); m_file = m_originalFilePath; } } else { ::org::kde::KDirNotify::emitFilesAdded(QUrl::fromLocalFile(m_url.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path())); m_uploadJob = 0; document->setModified( false ); emit document->completed(); m_saveOk = true; } m_duringSaveAs = false; m_originalURL = QUrl(); m_originalFilePath.clear(); if (m_waitForSave) { m_eventLoop.quit(); } } }; KisDocument::KisDocument() : d(new Private(this)) { d->undoStack = new UndoStack(this); d->undoStack->setParent(this); d->isEmpty = true; d->filterManager = new KisImportExportManager(this, d->progressUpdater); connect(&d->autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave())); setAutoSave(defaultAutoSave()); setObjectName(newObjectName()); d->docInfo = new KoDocumentInfo(this); d->pageLayout.width = 0; d->pageLayout.height = 0; d->pageLayout.topMargin = 0; d->pageLayout.bottomMargin = 0; d->pageLayout.leftMargin = 0; d->pageLayout.rightMargin = 0; KConfigGroup cfgGrp( KSharedConfig::openConfig(), "Undo"); d->undoStack->setUndoLimit(cfgGrp.readEntry("UndoLimit", 1000)); connect(d->undoStack, SIGNAL(indexChanged(int)), this, SLOT(slotUndoStackIndexChanged(int))); // preload the krita resources KisResourceServerProvider::instance(); init(); undoStack()->setUndoLimit(KisConfig().undoStackLimit()); setBackupFile(KisConfig().backupFile()); gridData().setShowGrid(false); KisConfig cfg; gridData().setGrid(cfg.getGridHSpacing(), cfg.getGridVSpacing()); } KisDocument::~KisDocument() { /** * Push a timebomb, which will try to release the memory after * the document has been deleted */ KisPaintDevice::createMemoryReleaseObject()->deleteLater(); d->autoSaveTimer.disconnect(this); d->autoSaveTimer.stop(); delete d->filterManager; // Despite being QObject they needs to be deleted before the image delete d->shapeController; delete d->koShapeController; if (d->image) { d->image->notifyAboutToBeDeleted(); } // The following line trigger the deletion of the image d->image.clear(); delete d; } void KisDocument::init() { delete d->nserver; d->nserver = 0; d->nserver = new KisNameServer(1); Q_CHECK_PTR(d->nserver); d->shapeController = new KisShapeController(this, d->nserver); d->koShapeController = new KoShapeController(0, d->shapeController); d->kraSaver = 0; d->kraLoader = 0; } bool KisDocument::reload() { // XXX: reimplement! return false; } bool KisDocument::exportDocument(const QUrl &_url) { bool ret; d->isExporting = true; // // Preserve a lot of state here because we need to restore it in order to // be able to fake a File --> Export. Can't do this in saveFile() because, // for a start, KParts has already set url and m_file and because we need // to restore the modified flag etc. and don't want to put a load on anyone // reimplementing saveFile() (Note: importDocument() and exportDocument() // will remain non-virtual). // QUrl oldURL = url(); QString oldFile = localFilePath(); bool wasModified = isModified(); QByteArray oldMimeType = mimeType(); // save... ret = saveAs(_url); // // This is sooooo hacky :( // Hopefully we will restore enough state. // dbgUI << "Restoring KisDocument state to before export"; // always restore url & m_file because KParts has changed them // (regardless of failure or success) setUrl(oldURL); setLocalFilePath(oldFile); // on successful export we need to restore modified etc. too // on failed export, mimetype/modified hasn't changed anyway if (ret) { setModified(wasModified); d->mimeType = oldMimeType; } d->isExporting = false; return ret; } bool KisDocument::saveFile() { dbgUI << "doc=" << url().url(); // Save it to be able to restore it after a failed save const bool wasModified = isModified(); // The output format is set by koMainWindow, and by openFile QByteArray outputMimeType = d->outputMimeType; if (outputMimeType.isEmpty()) outputMimeType = d->outputMimeType = nativeFormatMimeType(); QApplication::setOverrideCursor(Qt::WaitCursor); if (backupFile()) { if (url().isLocalFile()) KBackup::backupFile(url().toLocalFile(), d->backupPath); else { KIO::UDSEntry entry; if (KIO::NetAccess::stat(url(), entry, KisPart::instance()->currentMainwindow())) { // this file exists => backup emit statusBarMessage(i18n("Making backup...")); QUrl backup; if (d->backupPath.isEmpty()) backup = url(); else backup = QUrl::fromLocalFile(d->backupPath + '/' + url().fileName()); backup.setPath(backup.path() + QString::fromLatin1("~")); KFileItem item(entry, url()); Q_ASSERT(item.name() == url().fileName()); KIO::FileCopyJob *job = KIO::file_copy(url(), backup, item.permissions(), KIO::Overwrite | KIO::HideProgressInfo); job->exec(); } } } emit statusBarMessage(i18n("Saving...")); qApp->processEvents(); bool ret = false; bool suppressErrorDialog = false; if (!isNativeFormat(outputMimeType)) { dbgUI << "Saving to format" << outputMimeType << "in" << localFilePath(); // Not native format : save using export filter KisImportExportFilter::ConversionStatus status = d->filterManager->exportDocument(localFilePath(), outputMimeType); ret = status == KisImportExportFilter::OK; suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph); dbgFile << "Export status was" << status; } else { // Native format => normal save Q_ASSERT(!localFilePath().isEmpty()); ret = saveNativeFormat(localFilePath()); } if (ret) { d->undoStack->setClean(); removeAutoSaveFiles(); // Restart the autosave timer // (we don't want to autosave again 2 seconds after a real save) setAutoSave(d->autoSaveDelay); } QApplication::restoreOverrideCursor(); if (!ret) { if (!suppressErrorDialog) { if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save\n%1", localFilePath())); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not save %1\nReason: %2", localFilePath(), errorMessage())); } } // couldn't save file so this new URL is invalid // FIXME: we should restore the current document's true URL instead of // setting it to nothing otherwise anything that depends on the URL // being correct will not work (i.e. the document will be called // "Untitled" which may not be true) // // Update: now the URL is restored in KisMainWindow but really, this // should still be fixed in KisDocument/KParts (ditto for file). // We still resetURL() here since we may or may not have been called // by KisMainWindow - Clarence resetURL(); // As we did not save, restore the "was modified" status setModified(wasModified); } if (ret) { d->mimeType = outputMimeType; setConfirmNonNativeSave(isExporting(), false); } emit clearStatusBarMessage(); return ret; } QByteArray KisDocument::mimeType() const { return d->mimeType; } void KisDocument::setMimeType(const QByteArray & mimeType) { d->mimeType = mimeType; } void KisDocument::setOutputMimeType(const QByteArray & mimeType, int specialOutputFlag) { d->outputMimeType = mimeType; d->specialOutputFlag = specialOutputFlag; } QByteArray KisDocument::outputMimeType() const { return d->outputMimeType; } int KisDocument::specialOutputFlag() const { return d->specialOutputFlag; } bool KisDocument::confirmNonNativeSave(const bool exporting) const { // "exporting ? 1 : 0" is different from "exporting" because a bool is // usually implemented like an "int", not "unsigned : 1" return d->confirmNonNativeSave [ exporting ? 1 : 0 ]; } void KisDocument::setConfirmNonNativeSave(const bool exporting, const bool on) { d->confirmNonNativeSave [ exporting ? 1 : 0] = on; } bool KisDocument::saveInBatchMode() const { return d->filterManager->getBatchMode(); } void KisDocument::setSaveInBatchMode(const bool batchMode) { d->filterManager->setBatchMode(batchMode); } bool KisDocument::isImporting() const { return d->isImporting; } bool KisDocument::isExporting() const { return d->isExporting; } void KisDocument::setCheckAutoSaveFile(bool b) { d->shouldCheckAutoSaveFile = b; } void KisDocument::setAutoErrorHandlingEnabled(bool b) { d->autoErrorHandlingEnabled = b; } bool KisDocument::isAutoErrorHandlingEnabled() const { return d->autoErrorHandlingEnabled; } void KisDocument::slotAutoSave() { if (d->modified && d->modifiedAfterAutosave && !d->isLoading) { // Give a warning when trying to autosave an encrypted file when no password is known (should not happen) if (d->specialOutputFlag == SaveEncrypted && d->password.isNull()) { // That advice should also fix this error from occurring again emit statusBarMessage(i18n("The password of this encrypted document is not known. Autosave aborted! Please save your work manually.")); } else { connect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); emit statusBarMessage(i18n("Autosaving...")); d->autosaving = true; bool ret = saveNativeFormat(autoSaveFile(localFilePath())); setModified(true); if (ret) { d->modifiedAfterAutosave = false; d->autoSaveTimer.stop(); // until the next change } d->autosaving = false; emit clearStatusBarMessage(); disconnect(this, SIGNAL(sigProgress(int)), KisPart::instance()->currentMainwindow(), SLOT(slotProgress(int))); if (!ret && !d->disregardAutosaveFailure) { emit statusBarMessage(i18n("Error during autosave! Partition full?")); } } } } void KisDocument::setReadWrite(bool readwrite) { d->readwrite = readwrite; setAutoSave(d->autoSaveDelay); foreach(KisMainWindow *mainWindow, KisPart::instance()->mainWindows()) { mainWindow->setReadWrite(readwrite); } } void KisDocument::setAutoSave(int delay) { d->autoSaveDelay = delay; if (isReadWrite() && d->autoSaveDelay > 0) d->autoSaveTimer.start(d->autoSaveDelay * 1000); else d->autoSaveTimer.stop(); } KoDocumentInfo *KisDocument::documentInfo() const { return d->docInfo; } bool KisDocument::isModified() const { return d->modified; } bool KisDocument::saveNativeFormat(const QString & file) { const int realAutoSaveInterval = KisConfig().autoSaveInterval(); const int emergencyAutoSaveInterval = 10; // sec if (!d->image->tryBarrierLock()) { if (isAutosaving()) { setDisregardAutosaveFailure(true); if (realAutoSaveInterval) { setAutoSave(emergencyAutoSaveInterval); } return false; } else { d->image->requestStrokeEnd(); QApplication::processEvents(); if (!d->image->tryBarrierLock()) { return false; } } } setDisregardAutosaveFailure(false); d->lastErrorMessage.clear(); //dbgUI <<"Saving to store"; KoStore::Backend backend = KoStore::Auto; if (d->specialOutputFlag == SaveAsDirectoryStore) { backend = KoStore::Directory; dbgUI << "Saving as uncompressed XML, using directory store."; } else if (d->specialOutputFlag == SaveAsFlatXML) { dbgUI << "Saving as a flat XML file."; QFile f(file); if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { bool success = saveToStream(&f); f.close(); return success; } else return false; } dbgUI << "KisDocument::saveNativeFormat nativeFormatMimeType=" << nativeFormatMimeType(); // TODO: use std::auto_ptr or create store on stack [needs API fixing], // to remove all the 'delete store' in all the branches KoStore *store = KoStore::createStore(file, KoStore::Write, d->outputMimeType, backend); if (d->specialOutputFlag == SaveEncrypted && !d->password.isNull()) { store->setPassword(d->password); } if (store->bad()) { d->lastErrorMessage = i18n("Could not create the file for saving"); // more details needed? delete store; d->image->unlock(); setAutoSave(realAutoSaveInterval); return false; } d->image->unlock(); setAutoSave(realAutoSaveInterval); return saveNativeFormatCalligra(store); } bool KisDocument::saveNativeFormatCalligra(KoStore *store) { dbgUI << "Saving root"; if (store->open("root")) { KoStoreDevice dev(store); if (!saveToStream(&dev) || !store->close()) { dbgUI << "saveToStream failed"; delete store; return false; } } else { d->lastErrorMessage = i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml")); delete store; return false; } if (store->open("documentinfo.xml")) { QDomDocument doc = KisDocument::createDomDocument("document-info" /*DTD name*/, "document-info" /*tag name*/, "1.1"); doc = d->docInfo->save(doc); KoStoreDevice dev(store); QByteArray s = doc.toByteArray(); // this is already Utf8! (void)dev.write(s.data(), s.size()); (void)store->close(); } if (store->open("preview.png")) { // ### TODO: missing error checking (The partition could be full!) savePreview(store); (void)store->close(); } if (!completeSaving(store)) { delete store; return false; } dbgUI << "Saving done of url:" << url().url(); if (!store->finalize()) { delete store; return false; } // Success delete store; return true; } bool KisDocument::saveToStream(QIODevice *dev) { QDomDocument doc = saveXML(); // Save to buffer QByteArray s = doc.toByteArray(); // utf8 already dev->open(QIODevice::WriteOnly); int nwritten = dev->write(s.data(), s.size()); if (nwritten != (int)s.size()) warnUI << "wrote " << nwritten << "- expected" << s.size(); return nwritten == (int)s.size(); } QString KisDocument::checkImageMimeTypes(const QString &mimeType, const QUrl &url) const { if (!url.isLocalFile()) return mimeType; if (url.toLocalFile().endsWith(".kpp")) return "image/png"; QStringList imageMimeTypes; imageMimeTypes << "image/jpeg" << "image/x-psd" << "image/photoshop" << "image/x-photoshop" << "image/x-vnd.adobe.photoshop" << "image/vnd.adobe.photoshop" << "image/x-portable-pixmap" << "image/x-portable-graymap" << "image/x-portable-bitmap" << "application/pdf" << "image/x-exr" << "image/x-xcf" << "image/x-eps" << "image/png" << "image/bmp" << "image/x-xpixmap" << "image/gif" << "image/x-xbitmap" << "image/tiff" << "image/jp2"; if (!imageMimeTypes.contains(mimeType)) return mimeType; QFile f(url.toLocalFile()); if (!f.open(QIODevice::ReadOnly)) { warnKrita << "Could not open file to check the mimetype" << url; } QByteArray ba = f.read(qMin(f.size(), (qint64)512)); // should be enough for images QMimeDatabase db; QMimeType mime = db.mimeTypeForData(ba); f.close(); if (!mime.isValid()) { return mimeType; } // Checking the content failed as well, so let's fall back on the extension again if (mime.name() == "application/octet-stream") { return mimeType; } return mime.name(); } // Called for embedded documents bool KisDocument::saveToStore(KoStore *_store, const QString & _path) { dbgUI << "Saving document to store" << _path; _store->pushDirectory(); // Use the path as the internal url if (_path.startsWith(STORE_PROTOCOL)) setUrl(QUrl(_path)); else // ugly hack to pass a relative URI setUrl(QUrl(INTERNAL_PREFIX + _path)); // In the current directory we're the king :-) if (_store->open("root")) { KoStoreDevice dev(_store); if (!saveToStream(&dev)) { _store->close(); return false; } if (!_store->close()) return false; } if (!completeSaving(_store)) return false; // Now that we're done leave the directory again _store->popDirectory(); dbgUI << "Saved document to store"; return true; } bool KisDocument::savePreview(KoStore *store) { QPixmap pix = generatePreview(QSize(256, 256)); const QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly)); KoStoreDevice io(store); if (!io.open(QIODevice::WriteOnly)) return false; if (! preview.save(&io, "PNG")) // ### TODO What is -9 in quality terms? return false; io.close(); return true; } QPixmap KisDocument::generatePreview(const QSize& size) { if (d->image) { QRect bounds = d->image->bounds(); QSize newSize = bounds.size(); newSize.scale(size, Qt::KeepAspectRatio); QImage image; if (bounds.width() < 10000 && bounds.height() < 10000) { image = d->image->convertToQImage(d->image->bounds(), 0); } else { image = d->image->convertToQImage(QRect(0, 0, newSize.width(), newSize.height()), newSize, 0); } image = image.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); return QPixmap::fromImage(image); } return QPixmap(size); } QString KisDocument::autoSaveFile(const QString & path) const { QString retval; // Using the extension allows to avoid relying on the mime magic when opening QMimeDatabase db; QMimeType mime = db.mimeTypeForName(nativeFormatMimeType()); if (!mime.isValid()) { qFatal("It seems your installation is broken/incomplete because we failed to load the native mimetype \"%s\".", nativeFormatMimeType().constData()); } const QString extension = mime.preferredSuffix(); if (path.isEmpty()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::tempPath()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file retval = QString("%1/.%2-%3-%4-autosave%5").arg(QDir::homePath()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); #endif } else { QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); retval = QString("%1.%2-autosave%3").arg(dir).arg(filename).arg(extension); } return retval; } void KisDocument::setDisregardAutosaveFailure(bool disregardFailure) { d->disregardAutosaveFailure = disregardFailure; } bool KisDocument::importDocument(const QUrl &_url) { bool ret; dbgUI << "url=" << _url.url(); d->isImporting = true; // open... ret = openUrl(_url); // reset url & m_file (kindly? set by KisParts::openUrl()) to simulate a // File --> Import if (ret) { dbgUI << "success, resetting url"; resetURL(); setTitleModified(); } d->isImporting = false; return ret; } bool KisDocument::openUrl(const QUrl &_url, KisDocument::OpenUrlFlags flags) { dbgUI << "url=" << _url.url(); d->lastErrorMessage.clear(); // Reimplemented, to add a check for autosave files and to improve error reporting if (!_url.isValid()) { d->lastErrorMessage = i18n("Malformed URL\n%1", _url.url()); // ## used anywhere ? return false; } QUrl url(_url); bool autosaveOpened = false; d->isLoading = true; if (url.isLocalFile() && d->shouldCheckAutoSaveFile) { QString file = url.toLocalFile(); QString asf = autoSaveFile(file); if (QFile::exists(asf)) { //dbgUI <<"asf=" << asf; // ## TODO compare timestamps ? int res = QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("An autosaved file exists for this document.\nDo you want to open it instead?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes); switch (res) { case QMessageBox::Yes : url.setPath(asf); autosaveOpened = true; break; case QMessageBox::No : QFile::remove(asf); break; default: // Cancel d->isLoading = false; return false; } } } bool ret = openUrlInternal(url); if (autosaveOpened) { resetURL(); // Force save to act like 'Save As' setReadWrite(true); // enable save button setModified(true); } else { if( !(flags & OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES) ) { KisPart::instance()->addRecentURLToAllMainWindows(_url); } if (ret) { // Detect readonly local-files; remote files are assumed to be writable, unless we add a KIO::stat here (async). KFileItem file(url, mimeType(), KFileItem::Unknown); setReadWrite(file.isWritable()); } } return ret; } bool KisDocument::openFile() { //dbgUI <<"for" << localFilePath(); if (!QFile::exists(localFilePath())) { QApplication::restoreOverrideCursor(); if (d->autoErrorHandlingEnabled) // Maybe offer to create a new document with that name ? QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("File %1 does not exist.", localFilePath())); d->isLoading = false; return false; } QApplication::setOverrideCursor(Qt::WaitCursor); d->specialOutputFlag = 0; QByteArray _native_format = nativeFormatMimeType(); QUrl u = QUrl::fromLocalFile(localFilePath()); QString typeName = mimeType(); if (typeName.isEmpty()) { QMimeDatabase db; typeName = db.mimeTypeForFile(u.path()).name(); } // for images, always check content. typeName = checkImageMimeTypes(typeName, u); //dbgUI << "mimetypes 4:" << typeName; // Allow to open backup files, don't keep the mimetype application/x-trash. if (typeName == "application/x-trash") { QString path = u.path(); QMimeDatabase db; QMimeType mime = db.mimeTypeForName(typeName); const QStringList patterns = mime.isValid() ? mime.globPatterns() : QStringList(); // Find the extension that makes it a backup file, and remove it for (QStringList::ConstIterator it = patterns.begin(); it != patterns.end(); ++it) { QString ext = *it; if (!ext.isEmpty() && ext[0] == '*') { ext.remove(0, 1); if (path.endsWith(ext)) { path.chop(ext.length()); break; } } } typeName = db.mimeTypeForFile(path, QMimeDatabase::MatchExtension).name(); } // Special case for flat XML files (e.g. using directory store) if (u.fileName() == "maindoc.xml" || u.fileName() == "content.xml" || typeName == "inode/directory") { typeName = _native_format; // Hmm, what if it's from another app? ### Check mimetype d->specialOutputFlag = SaveAsDirectoryStore; dbgUI << "loading" << u.fileName() << ", using directory store for" << localFilePath() << "; typeName=" << typeName; } dbgUI << localFilePath() << "type:" << typeName; QString importedFile = localFilePath(); // create the main progress monitoring object for loading, this can // contain subtasks for filtering and loading KoProgressProxy *progressProxy = 0; if (d->progressProxy) { progressProxy = d->progressProxy; } d->progressUpdater = new KoProgressUpdater(progressProxy, KoProgressUpdater::Unthreaded, d->profileStream); d->progressUpdater->setReferenceTime(d->profileReferenceTime); d->progressUpdater->start(100, i18n("Opening Document")); setupOpenFileSubProgress(); if (!isNativeFormat(typeName.toLatin1())) { KisImportExportFilter::ConversionStatus status; importedFile = d->filterManager->importDocument(localFilePath(), typeName, status); if (status != KisImportExportFilter::OK) { QApplication::restoreOverrideCursor(); QString msg; switch (status) { case KisImportExportFilter::OK: break; case KisImportExportFilter::FilterCreationError: msg = i18n("Could not create the filter plugin"); break; case KisImportExportFilter::CreationError: msg = i18n("Could not create the output document"); break; case KisImportExportFilter::FileNotFound: msg = i18n("File not found"); break; case KisImportExportFilter::StorageCreationError: msg = i18n("Cannot create storage"); break; case KisImportExportFilter::BadMimeType: msg = i18n("Bad MIME type"); break; case KisImportExportFilter::EmbeddedDocError: msg = i18n("Error in embedded document"); break; case KisImportExportFilter::WrongFormat: msg = i18n("Format not recognized"); break; case KisImportExportFilter::NotImplemented: msg = i18n("Not implemented"); break; case KisImportExportFilter::ParsingError: msg = i18n("Parsing error"); break; case KisImportExportFilter::PasswordProtected: msg = i18n("Document is password protected"); break; case KisImportExportFilter::InvalidFormat: msg = i18n("Invalid file format"); break; case KisImportExportFilter::InternalError: case KisImportExportFilter::UnexpectedEOF: case KisImportExportFilter::UnexpectedOpcode: case KisImportExportFilter::StupidError: // ?? what is this ?? case KisImportExportFilter::UsageError: msg = i18n("Internal error"); break; case KisImportExportFilter::OutOfMemory: msg = i18n("Out of memory"); break; case KisImportExportFilter::FilterEntryNull: msg = i18n("Empty Filter Plugin"); break; case KisImportExportFilter::NoDocumentCreated: msg = i18n("Trying to load into the wrong kind of document"); break; case KisImportExportFilter::DownloadFailed: msg = i18n("Failed to download remote file"); break; case KisImportExportFilter::UserCancelled: case KisImportExportFilter::BadConversionGraph: // intentionally we do not prompt the error message here break; default: msg = i18n("Unknown error"); break; } if (d->autoErrorHandlingEnabled && !msg.isEmpty()) { QString errorMsg(i18n("Could not open %2.\nReason: %1.\n%3", msg, prettyPathOrUrl(), errorMessage())); QMessageBox::critical(0, i18nc("@title:window", "Krita"), errorMsg); } d->isLoading = false; delete d->progressUpdater; d->progressUpdater = 0; return false; } d->isEmpty = false; dbgUI << "importedFile" << importedFile << "status:" << static_cast(status); } QApplication::restoreOverrideCursor(); bool ok = true; if (!importedFile.isEmpty()) { // Something to load (tmp or native file) ? // The filter, if any, has been applied. It's all native format now. if (!loadNativeFormat(importedFile)) { ok = false; if (d->autoErrorHandlingEnabled) { showLoadingErrorDialog(); } } } if (importedFile != localFilePath()) { // We opened a temporary file (result of an import filter) // Set document URL to empty - we don't want to save in /tmp ! // But only if in readwrite mode (no saving problem otherwise) // -- // But this isn't true at all. If this is the result of an // import, then importedFile=temporary_file.kwd and // file/m_url=foreignformat.ext so m_url is correct! // So don't resetURL() or else the caption won't be set when // foreign files are opened (an annoying bug). // - Clarence // #if 0 if (isReadWrite()) resetURL(); #endif // remove temp file - uncomment this to debug import filters if (!importedFile.isEmpty()) { #ifndef NDEBUG if (!getenv("CALLIGRA_DEBUG_FILTERS")) #endif QFile::remove(importedFile); } } if (ok) { setMimeTypeAfterLoading(typeName); emit sigLoadingFinished(); } if (progressUpdater()) { QPointer updater = progressUpdater()->startSubtask(1, "clear undo stack"); updater->setProgress(0); undoStack()->clear(); updater->setProgress(100); } delete d->progressUpdater; d->progressUpdater = 0; d->isLoading = false; return ok; } KoProgressUpdater *KisDocument::progressUpdater() const { return d->progressUpdater; } void KisDocument::setProgressProxy(KoProgressProxy *progressProxy) { d->progressProxy = progressProxy; } KoProgressProxy* KisDocument::progressProxy() const { if (!d->progressProxy) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainwindowCount() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } d->progressProxy = new DocumentProgressProxy(mainWindow); } return d->progressProxy; } // shared between openFile and koMainWindow's "create new empty document" code void KisDocument::setMimeTypeAfterLoading(const QString& mimeType) { d->mimeType = mimeType.toLatin1(); d->outputMimeType = d->mimeType; const bool needConfirm = !isNativeFormat(d->mimeType); setConfirmNonNativeSave(false, needConfirm); setConfirmNonNativeSave(true, needConfirm); } // The caller must call store->close() if loadAndParse returns true. bool KisDocument::oldLoadAndParse(KoStore *store, const QString& filename, KoXmlDocument& doc) { //dbgUI <<"Trying to open" << filename; if (!store->open(filename)) { warnUI << "Entry " << filename << " not found!"; d->lastErrorMessage = i18n("Could not find %1", filename); return false; } // Error variables for QDomDocument::setContent QString errorMsg; int errorLine, errorColumn; bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); store->close(); if (!ok) { errUI << "Parsing error in " << filename << "! Aborting!" << endl << " In line: " << errorLine << ", column: " << errorColumn << endl << " Error message: " << errorMsg << endl; d->lastErrorMessage = i18n("Parsing error in %1 at line %2, column %3\nError message: %4" , filename , errorLine, errorColumn , QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0, QCoreApplication::UnicodeUTF8)); return false; } dbgUI << "File" << filename << " loaded and parsed"; return true; } bool KisDocument::loadNativeFormat(const QString & file_) { QString file = file_; QFileInfo fileInfo(file); if (!fileInfo.exists()) { // check duplicated from openUrl, but this is useful for templates d->lastErrorMessage = i18n("The file %1 does not exist.", file); return false; } if (!fileInfo.isFile()) { file += "/content.xml"; QFileInfo fileInfo2(file); if (!fileInfo2.exists() || !fileInfo2.isFile()) { d->lastErrorMessage = i18n("%1 is not a file." , file_); return false; } } QApplication::setOverrideCursor(Qt::WaitCursor); dbgUI << file; QFile in; bool isRawXML = false; if (d->specialOutputFlag != SaveAsDirectoryStore) { // Don't try to open a directory ;) in.setFileName(file); if (!in.open(QIODevice::ReadOnly)) { QApplication::restoreOverrideCursor(); d->lastErrorMessage = i18n("Could not open the file for reading (check read permissions)."); return false; } char buf[6]; buf[5] = 0; int pos = 0; do { if (in.read(buf + pos , 1) < 1) { QApplication::restoreOverrideCursor(); in.close(); d->lastErrorMessage = i18n("Could not read the beginning of the file."); return false; } if (QChar(buf[pos]).isSpace()) continue; pos++; } while (pos < 5); isRawXML = (qstrnicmp(buf, "lastErrorMessage = i18n("parsing error in the main document at line %1, column %2\nError message: %3", errorLine, errorColumn, i18n(errorMsg.toUtf8())); res = false; } QApplication::restoreOverrideCursor(); in.close(); d->isEmpty = false; return res; } else { // It's a calligra store (tar.gz, zip, directory, etc.) in.close(); KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; KoStore *store = KoStore::createStore(file, KoStore::Read, "", backend); if (store->bad()) { d->lastErrorMessage = i18n("Not a valid Krita file: %1", file); delete store; QApplication::restoreOverrideCursor(); return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; const bool success = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (success && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return success; } } bool KisDocument::loadNativeFormatFromByteArray(QByteArray &data) { bool succes; KoStore::Backend backend = (d->specialOutputFlag == SaveAsDirectoryStore) ? KoStore::Directory : KoStore::Auto; QBuffer buffer(&data); KoStore *store = KoStore::createStore(&buffer, KoStore::Read, "", backend); if (store->bad()) { delete store; return false; } // Remember that the file was encrypted if (d->specialOutputFlag == 0 && store->isEncrypted() && !d->isImporting) d->specialOutputFlag = SaveEncrypted; succes = loadNativeFormatFromStoreInternal(store); // Retrieve the password after loading the file, only then is it guaranteed to exist if (succes && store->isEncrypted() && !d->isImporting) d->password = store->password(); delete store; return succes; } bool KisDocument::loadNativeFormatFromStoreInternal(KoStore *store) { if (store->hasFile("root") || store->hasFile("maindoc.xml")) { // Fallback to "old" file format (maindoc.xml) KoXmlDocument doc = KoXmlDocument(true); bool ok = oldLoadAndParse(store, "root", doc); if (ok) ok = loadXML(doc, store); if (!ok) { QApplication::restoreOverrideCursor(); return false; } } else { errUI << "ERROR: No maindoc.xml" << endl; d->lastErrorMessage = i18n("Invalid document: no file 'maindoc.xml'."); QApplication::restoreOverrideCursor(); return false; } if (store->hasFile("documentinfo.xml")) { KoXmlDocument doc = KoXmlDocument(true); if (oldLoadAndParse(store, "documentinfo.xml", doc)) { d->docInfo->load(doc); } } else { //dbgUI <<"cannot open document info"; delete d->docInfo; d->docInfo = new KoDocumentInfo(this); } bool res = completeLoading(store); QApplication::restoreOverrideCursor(); d->isEmpty = false; return res; } // For embedded documents bool KisDocument::loadFromStore(KoStore *_store, const QString& url) { if (_store->open(url)) { KoXmlDocument doc = KoXmlDocument(true); doc.setContent(_store->device()); if (!loadXML(doc, _store)) { _store->close(); return false; } _store->close(); } else { dbgKrita << "couldn't open " << url; } _store->pushDirectory(); // Store as document URL if (url.startsWith(STORE_PROTOCOL)) { setUrl(QUrl::fromUserInput(url)); } else { setUrl(QUrl(INTERNAL_PREFIX + url)); _store->enterDirectory(url); } bool result = completeLoading(_store); // Restore the "old" path _store->popDirectory(); return result; } bool KisDocument::loadOdf(KoOdfReadStore & odfStore) { Q_UNUSED(odfStore); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::saveOdf(SavingContext &documentContext) { Q_UNUSED(documentContext); setErrorMessage(i18n("Krita does not support the OpenDocument file format.")); return false; } bool KisDocument::isStoredExtern() const { return !storeInternal() && hasExternURL(); } void KisDocument::setModified() { d->modified = true; } void KisDocument::setModified(bool mod) { if (isAutosaving()) // ignore setModified calls due to autosaving return; if ( !d->readwrite && d->modified ) { errKrita << "Can't set a read-only document to 'modified' !" << endl; return; } //dbgUI<<" url:" << url.path(); //dbgUI<<" mod="<image) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } d->kraLoader->loadBinaryData(store, d->image, url().url(), isStoredExtern()); bool retval = true; if (!d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); retval = false; } if (retval) { vKisNodeSP preselectedNodes = d->kraLoader->selectedNodes(); if (preselectedNodes.size() > 0) { d->preActivatedNode = preselectedNodes.first(); } // before deleting the kraloader, get the list with preloaded assistants and save it d->assistants = d->kraLoader->assistants(); d->shapeController->setImage(d->image); connect(d->image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); if (d->image) { d->image->initialRefreshGraph(); } setAutoSave(KisConfig().autoSaveInterval()); emit sigLoadingFinished(); } delete d->kraLoader; d->kraLoader = 0; return retval; } bool KisDocument::completeSaving(KoStore* store) { QString uri = url().url(); d->kraSaver->saveBinaryData(store, d->image, url().url(), isStoredExtern(), isAutosaving()); bool retval = true; if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); retval = false; } delete d->kraSaver; d->kraSaver = 0; emit sigSavingFinished(); return retval; } QDomDocument KisDocument::createDomDocument(const QString& tagName, const QString& version) const { return createDomDocument("krita", tagName, version); } //static QDomDocument KisDocument::createDomDocument(const QString& appName, const QString& tagName, const QString& version) { QDomImplementation impl; QString url = QString("http://www.calligra.org/DTD/%1-%2.dtd").arg(appName).arg(version); QDomDocumentType dtype = impl.createDocumentType(tagName, QString("-//KDE//DTD %1 %2//EN").arg(appName).arg(version), url); // The namespace URN doesn't need to include the version number. QString namespaceURN = QString("http://www.calligra.org/DTD/%1").arg(appName); QDomDocument doc = impl.createDocument(namespaceURN, tagName, dtype); doc.insertBefore(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""), doc.documentElement()); return doc; } bool KisDocument::loadXML(const KoXmlDocument& doc, KoStore *store) { Q_UNUSED(store); if (d->image) { d->shapeController->setImage(0); d->image = 0; } KoXmlElement root; KoXmlNode node; KisImageWSP image; init(); if (doc.doctype().name() != "DOC") { setErrorMessage(i18n("The format is not supported or the file is corrupted")); return false; } root = doc.documentElement(); int syntaxVersion = root.attribute("syntaxVersion", "3").toInt(); if (syntaxVersion > 2) { setErrorMessage(i18n("The file is too new for this version of Krita (%1).", syntaxVersion)); return false; } if (!root.hasChildNodes()) { setErrorMessage(i18n("The file has no layers.")); return false; } if (d->kraLoader) delete d->kraLoader; d->kraLoader = new KisKraLoader(this, syntaxVersion); // Legacy from the multi-image .kra file period. for (node = root.firstChild(); !node.isNull(); node = node.nextSibling()) { if (node.isElement()) { if (node.nodeName() == "IMAGE") { KoXmlElement elem = node.toElement(); if (!(image = d->kraLoader->loadXML(elem))) { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("Unknown error.")); } else { setErrorMessage(d->kraLoader->errorMessages().join(".\n")); } return false; } } else { if (d->kraLoader->errorMessages().isEmpty()) { setErrorMessage(i18n("The file does not contain an image.")); } return false; } } } if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); } d->image = image; return true; } QDomDocument KisDocument::saveXML() { dbgFile << url(); QDomDocument doc = createDomDocument("DOC", CURRENT_DTD_VERSION); QDomElement root = doc.documentElement(); root.setAttribute("editor", "Krita"); root.setAttribute("syntaxVersion", "2"); if (d->kraSaver) delete d->kraSaver; d->kraSaver = new KisKraSaver(this); root.appendChild(d->kraSaver->saveXML(doc, d->image)); if (!d->kraSaver->errorMessages().isEmpty()) { setErrorMessage(d->kraSaver->errorMessages().join(".\n")); } return doc; } bool KisDocument::isNativeFormat(const QByteArray& mimetype) const { if (mimetype == nativeFormatMimeType()) return true; return extraNativeMimeTypes().contains(mimetype); } int KisDocument::supportedSpecialFormats() const { return 0; // we don't support encryption. } void KisDocument::setErrorMessage(const QString& errMsg) { d->lastErrorMessage = errMsg; } QString KisDocument::errorMessage() const { return d->lastErrorMessage; } void KisDocument::showLoadingErrorDialog() { if (errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open\n%1", localFilePath())); } else if (errorMessage() != "USER_CANCELED") { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not open %1\nReason: %2", localFilePath(), errorMessage())); } } bool KisDocument::isAutosaving() const { return d->autosaving; } bool KisDocument::isLoading() const { return d->isLoading; } void KisDocument::removeAutoSaveFiles() { // Eliminate any auto-save file QString asf = autoSaveFile(localFilePath()); // the one in the current dir if (QFile::exists(asf)) QFile::remove(asf); asf = autoSaveFile(QString()); // and the one in $HOME if (QFile::exists(asf)) QFile::remove(asf); } void KisDocument::setBackupFile(bool _b) { d->backupFile = _b; } bool KisDocument::backupFile()const { return d->backupFile; } void KisDocument::setBackupPath(const QString & _path) { d->backupPath = _path; } QString KisDocument::backupPath()const { return d->backupPath; } bool KisDocument::storeInternal() const { return d->storeInternal; } void KisDocument::setStoreInternal(bool i) { d->storeInternal = i; //dbgUI<<"="<storeInternal<<" doc:"<pageLayout; } void KisDocument::setPageLayout(const KoPageLayout &pageLayout) { d->pageLayout = pageLayout; } KoUnit KisDocument::unit() const { return d->unit; } void KisDocument::setUnit(const KoUnit &unit) { if (d->unit != unit) { d->unit = unit; emit unitChanged(unit); } } void KisDocument::saveUnitOdf(KoXmlWriter *settingsWriter) const { settingsWriter->addConfigItem("unit", unit().symbol()); } KUndo2Stack *KisDocument::undoStack() { return d->undoStack; } void KisDocument::addCommand(KUndo2Command *command) { if (command) d->undoStack->push(command); } void KisDocument::beginMacro(const KUndo2MagicString & text) { d->undoStack->beginMacro(text); } void KisDocument::endMacro() { d->undoStack->endMacro(); } void KisDocument::slotUndoStackIndexChanged(int idx) { // even if the document was already modified, call setModified to re-start autosave timer setModified(idx != d->undoStack->cleanIndex()); } void KisDocument::setProfileStream(QTextStream *profilestream) { d->profileStream = profilestream; } void KisDocument::setProfileReferenceTime(const QTime& referenceTime) { d->profileReferenceTime = referenceTime; } void KisDocument::clearUndoHistory() { d->undoStack->clear(); } KoGridData &KisDocument::gridData() { return d->gridData; } KoGuidesData &KisDocument::guidesData() { return d->guidesData; } bool KisDocument::isEmpty() const { return d->isEmpty; } void KisDocument::setEmpty() { d->isEmpty = true; } // static int KisDocument::defaultAutoSave() { return 300; } void KisDocument::resetURL() { setUrl(QUrl()); setLocalFilePath(QString()); } int KisDocument::pageCount() const { return 1; } void KisDocument::setupOpenFileSubProgress() {} KoDocumentInfoDlg *KisDocument::createDocumentInfoDialog(QWidget *parent, KoDocumentInfo *docInfo) const { return new KoDocumentInfoDlg(parent, docInfo); } bool KisDocument::isReadWrite() const { return d->readwrite; } QUrl KisDocument::url() const { return d->m_url; } bool KisDocument::closeUrl(bool promptToSave) { if (promptToSave) { if ( d->document->isReadWrite() && d->document->isModified()) { foreach(KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { if (!view->queryClose()) { return false; } } } } } // Not modified => ok and delete temp file. d->mimeType = QByteArray(); if ( d->m_bTemp ) { QFile::remove( d->m_file ); d->m_bTemp = false; } // It always succeeds for a read-only part, // but the return value exists for reimplementations // (e.g. pressing cancel for a modified read-write part) return true; } bool KisDocument::saveAs( const QUrl &kurl ) { if (!kurl.isValid()) { errKrita << "saveAs: Malformed URL " << kurl.url() << endl; return false; } d->m_duringSaveAs = true; d->m_originalURL = d->m_url; d->m_originalFilePath = d->m_file; d->m_url = kurl; // Store where to upload in saveToURL d->prepareSaving(); bool result = save(); // Save local file and upload local file if (!result) { d->m_url = d->m_originalURL; d->m_file = d->m_originalFilePath; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); } return result; } bool KisDocument::save() { d->m_saveOk = false; if ( d->m_file.isEmpty() ) // document was created empty d->prepareSaving(); DocumentProgressProxy *progressProxy = 0; if (!d->document->progressProxy()) { KisMainWindow *mainWindow = 0; if (KisPart::instance()->mainwindowCount() > 0) { mainWindow = KisPart::instance()->mainWindows()[0]; } progressProxy = new DocumentProgressProxy(mainWindow); d->document->setProgressProxy(progressProxy); } d->document->setUrl(url()); // THIS IS WRONG! KisDocument::saveFile should move here, and whoever subclassed KisDocument to // reimplement saveFile shold now subclass KisPart. bool ok = d->document->saveFile(); if (progressProxy) { d->document->setProgressProxy(0); delete progressProxy; } if (ok) { return saveToUrl(); } else { emit canceled(QString()); } return false; } bool KisDocument::waitSaveComplete() { if (!d->m_uploadJob) return d->m_saveOk; d->m_waitForSave = true; d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); d->m_waitForSave = false; return d->m_saveOk; } void KisDocument::setUrl(const QUrl &url) { d->m_url = url; } QString KisDocument::localFilePath() const { return d->m_file; } void KisDocument::setLocalFilePath( const QString &localFilePath ) { d->m_file = localFilePath; } bool KisDocument::saveToUrl() { if ( d->m_url.isLocalFile() ) { d->document->setModified( false ); emit completed(); // if m_url is a local file there won't be a temp file -> nothing to remove Q_ASSERT( !d->m_bTemp ); d->m_saveOk = true; d->m_duringSaveAs = false; d->m_originalURL = QUrl(); d->m_originalFilePath.clear(); return true; // Nothing to do } #ifndef Q_OS_WIN else { if (d->m_uploadJob) { QFile::remove(d->m_uploadJob->srcUrl().toLocalFile()); d->m_uploadJob->kill(); d->m_uploadJob = 0; } QTemporaryFile *tempFile = new QTemporaryFile(); tempFile->open(); QString uploadFile = tempFile->fileName(); delete tempFile; QUrl uploadUrl; uploadUrl.setPath( uploadFile ); // Create hardlink if (::link(QFile::encodeName(d->m_file), QFile::encodeName(uploadFile)) != 0) { // Uh oh, some error happened. return false; } d->m_uploadJob = KIO::file_move( uploadUrl, d->m_url, -1, KIO::Overwrite ); connect( d->m_uploadJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotUploadFinished(KJob*)) ); return true; } #else return false; #endif } bool KisDocument::openUrlInternal(const QUrl &url) { if ( !url.isValid() ) return false; if (d->m_bAutoDetectedMime) { d->mimeType = QByteArray(); d->m_bAutoDetectedMime = false; } QByteArray mimetype = d->mimeType; if ( !closeUrl() ) return false; d->mimeType = mimetype; setUrl(url); d->m_file.clear(); if (d->m_url.isLocalFile()) { d->m_file = d->m_url.toLocalFile(); return d->openLocalFile(); } else { d->openRemoteFile(); return true; } } KisImageWSP KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* colorspace) { KoColor backgroundColor(Qt::white, colorspace); /** * FIXME: check whether this is a good value */ double defaultResolution=1.; newImage(name, width, height, colorspace, backgroundColor, "", defaultResolution); return image(); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace * cs, const KoColor &bgColor, const QString &imageDescription, const double imageResolution) { return newImage(name, width, height, cs, bgColor, false, 1, imageDescription, imageResolution); } bool KisDocument::newImage(const QString& name, qint32 width, qint32 height, const KoColorSpace* cs, const KoColor &bgColor, bool backgroundAsLayer, int numberOfLayers, const QString &description, const double imageResolution) { Q_ASSERT(cs); init(); KisConfig cfg; KisImageWSP image; KisPaintLayerSP layer; if (!cs) return false; QApplication::setOverrideCursor(Qt::BusyCursor); image = new KisImage(createUndoStore(), width, height, cs, name); Q_CHECK_PTR(image); connect(image.data(), SIGNAL(sigImageModified()), this, SLOT(setImageModified())); image->setResolution(imageResolution, imageResolution); image->assignImageProfile(cs->profile()); documentInfo()->setAboutInfo("title", name); if (name != i18n("Unnamed") && !name.isEmpty()) { setUrl(QUrl(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation) + '/' + name + ".kra")); } documentInfo()->setAboutInfo("comments", description); layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, cs); Q_CHECK_PTR(layer); if (backgroundAsLayer) { image->setDefaultProjectionColor(KoColor(cs)); if (bgColor.opacityU8() == OPACITY_OPAQUE_U8) { layer->paintDevice()->setDefaultPixel(bgColor.data()); } else { // Hack: with a semi-transparent background color, the projection isn't composited right if we just set the default pixel KisFillPainter painter; painter.begin(layer->paintDevice()); painter.fillRect(0, 0, width, height, bgColor, bgColor.opacityU8()); } } else { image->setDefaultProjectionColor(bgColor); } layer->setDirty(QRect(0, 0, width, height)); image->addNode(layer.data(), image->rootLayer().data()); setCurrentImage(image); for(int i = 1; i < numberOfLayers; ++i) { KisPaintLayerSP layer = new KisPaintLayer(image, image->nextLayerName(), OPACITY_OPAQUE_U8, cs); image->addNode(layer, image->root(), i); layer->setDirty(QRect(0, 0, width, height)); } cfg.defImageWidth(width); cfg.defImageHeight(height); cfg.defImageResolution(imageResolution); cfg.defColorModel(image->colorSpace()->colorModelId().id()); cfg.setDefaultColorDepth(image->colorSpace()->colorDepthId().id()); cfg.defColorProfile(image->colorSpace()->profile()->name()); QApplication::restoreOverrideCursor(); return true; } KoShapeBasedDocumentBase *KisDocument::shapeController() const { return d->shapeController; } KoShapeLayer* KisDocument::shapeForNode(KisNodeSP layer) const { return d->shapeController->shapeForNode(layer); } vKisNodeSP KisDocument::activeNodes() const { vKisNodeSP nodes; foreach(KisView *v, KisPart::instance()->views()) { if (v->document() == this && v->viewManager()) { KisNodeSP activeNode = v->viewManager()->activeNode(); if (activeNode && !nodes.contains(activeNode)) { if (activeNode->inherits("KisMask")) { activeNode = activeNode->parent(); } nodes.append(activeNode); } } } return nodes; } QList KisDocument::assistants() { QList assistants; foreach(KisView *view, KisPart::instance()->views()) { if (view && view->document() == this) { KisPaintingAssistantsDecoration* assistantsDecoration = view->canvasBase()->paintingAssistantsDecoration(); assistants.append(assistantsDecoration->assistants()); } } return assistants; } QList KisDocument::preLoadedAssistants() { return d->assistants; } void KisDocument::setPreActivatedNode(KisNodeSP activatedNode) { d->preActivatedNode = activatedNode; } KisNodeSP KisDocument::preActivatedNode() const { return d->preActivatedNode; } void KisDocument::prepareForImport() { if (d->nserver == 0) { init(); } } KisImageWSP KisDocument::image() const { return d->image; } void KisDocument::setCurrentImage(KisImageWSP image) { if (!image || !image.isValid()) return; if (d->image) { // Disconnect existing sig/slot connections d->image->disconnect(this); d->shapeController->setImage(0); } d->image = image; d->shapeController->setImage(image); setModified(false); connect(d->image, SIGNAL(sigImageModified()), this, SLOT(setImageModified())); d->image->initialRefreshGraph(); setAutoSave(KisConfig().autoSaveInterval()); } void KisDocument::initEmpty() { KisConfig cfg; const KoColorSpace * rgb = KoColorSpaceRegistry::instance()->rgb8(); newImage("", cfg.defImageWidth(), cfg.defImageHeight(), rgb); } void KisDocument::setImageModified() { setModified(true); } KisUndoStore* KisDocument::createUndoStore() { return new KisDocumentUndoStore(this); } #include #include #include diff --git a/krita/ui/KisDocumentEntry.h b/krita/ui/KisDocumentEntry.h index 079a994bac..c77725c1fb 100644 --- a/krita/ui/KisDocumentEntry.h +++ b/krita/ui/KisDocumentEntry.h @@ -1,98 +1,97 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright 2007 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_DOCUMENT_ENTRY_H #define KIS_DOCUMENT_ENTRY_H #include #include #include "kritaui_export.h" class QStringList; -class KisDocument; class QPluginLoader; /** * Represents an available Calligra component * that supports the document interface. */ class KRITAUI_EXPORT KisDocumentEntry { public: /** * Represents an invalid entry (as returned by queryByMimeType for instance) */ explicit KisDocumentEntry(); /** * Represents a valid entry */ explicit KisDocumentEntry(QPluginLoader *loader); ~KisDocumentEntry(); static QString nativeMimeType(); static QStringList extraNativeMimeTypes(); QPluginLoader *loader() const; /** * @return TRUE if the service pointer is null */ bool isEmpty() const; /** * @return name of the associated service */ QString name() const; /** * Mimetypes (and other service types) which this document can handle. */ QStringList mimeTypes() const; /** * @return TRUE if the document can handle the requested mimetype. */ bool supportsMimeType(const QString & _mimetype) const; /** * This function will query ksycoca to find all available components. * The result will only contain parts, which are embeddable into a document * * @param _constr is a constraint expression as used by KTrader. * You can use it to set additional restrictions on the available * components. */ static QList query(const QString & mimetype = QString()); /** * This is a convenience function. * * @return a document entry for the Calligra component that supports * the requested mimetype and fits the user best. */ static KisDocumentEntry queryByMimeType(const QString & mimetype); private: QPluginLoader *m_loader; }; #endif diff --git a/krita/ui/KisFilterChain.cpp b/krita/ui/KisFilterChain.cpp index 1c0313ce45..c12242735e 100644 --- a/krita/ui/KisFilterChain.cpp +++ b/krita/ui/KisFilterChain.cpp @@ -1,543 +1,542 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisFilterChain.h" #include "KisImportExportManager.h" // KisImportExportManager::filterAvailable, private API #include "KisDocumentEntry.h" -#include "KisFilterEntry.h" #include "KisDocument.h" #include "KisPart.h" #include "PriorityQueue_p.h" #include "KisFilterGraph.h" #include "KisFilterEdge.h" #include "KisFilterChainLink.h" #include "KisFilterVertex.h" #include #include #include #include // UINT_MAX #include #include // Those "defines" are needed in the setupConnections method below. // Please always keep the strings and the length in sync! using namespace CalligraFilter; KisFilterChain::KisFilterChain(const KisImportExportManager* manager) : KisShared() , m_manager(manager) , m_state(Beginning) , m_inputStorage(0), m_inputStorageDevice(0) , m_outputStorage(0) , m_outputStorageDevice(0) , m_inputDocument(0) , m_outputDocument(0) , m_inputTempFile(0), m_outputTempFile(0) , m_inputQueried(Nil) , m_outputQueried(Nil) , d(0) { } KisFilterChain::~KisFilterChain() { m_chainLinks.deleteAll(); if (filterManagerParentChain() && filterManagerParentChain()->m_outputStorage) filterManagerParentChain()->m_outputStorage->leaveDirectory(); manageIO(); // Called for the 2nd time in a row -> clean up } KisImportExportFilter::ConversionStatus KisFilterChain::invokeChain() { KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK; m_state = Beginning; int count = m_chainLinks.count(); // This is needed due to nasty Microsoft design const ChainLink* parentChainLink = 0; if (filterManagerParentChain()) parentChainLink = filterManagerParentChain()->m_chainLinks.current(); // No iterator here, as we need m_chainLinks.current() in outputDocument() m_chainLinks.first(); for (; count > 1 && m_chainLinks.current() && status == KisImportExportFilter::OK; m_chainLinks.next(), --count) { status = m_chainLinks.current()->invokeFilter(parentChainLink); m_state = Middle; manageIO(); } if (!m_chainLinks.current()) { warnFile << "Huh?? Found a null pointer in the chain"; return KisImportExportFilter::StupidError; } if (status == KisImportExportFilter::OK) { if (m_state & Beginning) m_state |= End; else m_state = End; status = m_chainLinks.current()->invokeFilter(parentChainLink); manageIO(); } m_state = Done; if (status == KisImportExportFilter::OK) finalizeIO(); return status; } QString KisFilterChain::chainOutput() const { if (m_state == Done) return m_inputFile; // as we already called manageIO() return QString(); } QString KisFilterChain::inputFile() { if (m_inputQueried == File) return m_inputFile; else if (m_inputQueried != Nil) { warnFile << "You already asked for some different source."; return QString(); } m_inputQueried = File; if (m_state & Beginning) { if (static_cast(filterManagerDirection()) == KisImportExportManager::Import) m_inputFile = filterManagerImportFile(); else inputFileHelper(filterManagerKisDocument(), filterManagerImportFile()); } else if (m_inputFile.isEmpty()) inputFileHelper(m_inputDocument, QString()); return m_inputFile; } QString KisFilterChain::outputFile() { // sanity check: No embedded filter should ask for a plain file // ###### CHECK: This will break as soon as we support exporting embedding filters if (filterManagerParentChain()) warnFile << "An embedded filter has to use storageFile()!"; if (m_outputQueried == File) return m_outputFile; else if (m_outputQueried != Nil) { warnFile << "You already asked for some different destination."; return QString(); } m_outputQueried = File; if (m_state & End) { if (static_cast(filterManagerDirection()) == KisImportExportManager::Import) outputFileHelper(false); // This (last) one gets deleted by the caller else m_outputFile = filterManagerExportFile(); } else outputFileHelper(true); return m_outputFile; } KoStoreDevice* KisFilterChain::storageFile(const QString& name, KoStore::Mode mode) { // Plain normal use case if (m_inputQueried == Storage && mode == KoStore::Read && m_inputStorage && m_inputStorage->mode() == KoStore::Read) return storageNewStreamHelper(&m_inputStorage, &m_inputStorageDevice, name); else if (m_outputQueried == Storage && mode == KoStore::Write && m_outputStorage && m_outputStorage->mode() == KoStore::Write) return storageNewStreamHelper(&m_outputStorage, &m_outputStorageDevice, name); else if (m_inputQueried == Nil && mode == KoStore::Read) return storageHelper(inputFile(), name, KoStore::Read, &m_inputStorage, &m_inputStorageDevice); else if (m_outputQueried == Nil && mode == KoStore::Write) return storageHelper(outputFile(), name, KoStore::Write, &m_outputStorage, &m_outputStorageDevice); else { warnFile << "Oooops, how did we get here? You already asked for a" << " different source/destination?" << endl; return 0; } } KisDocument* KisFilterChain::inputDocument() { if (m_inputQueried == Document) return m_inputDocument; else if (m_inputQueried != Nil) { warnFile << "You already asked for some different source."; return 0; } if ((m_state & Beginning) && static_cast(filterManagerDirection()) == KisImportExportManager::Export && filterManagerKisDocument()) m_inputDocument = filterManagerKisDocument(); else if (!m_inputDocument) m_inputDocument = createDocument(inputFile()); m_inputQueried = Document; return m_inputDocument; } KisDocument* KisFilterChain::outputDocument() { // sanity check: No embedded filter should ask for a document // ###### CHECK: This will break as soon as we support exporting embedding filters if (filterManagerParentChain()) { warnFile << "An embedded filter has to use storageFile()!"; return 0; } if (m_outputQueried == Document) return m_outputDocument; else if (m_outputQueried != Nil) { warnFile << "You already asked for some different destination."; return 0; } if ((m_state & End) && static_cast(filterManagerDirection()) == KisImportExportManager::Import && filterManagerKisDocument()) m_outputDocument = filterManagerKisDocument(); else m_outputDocument = createDocument(m_chainLinks.current()->to()); m_outputQueried = Document; return m_outputDocument; } void KisFilterChain::dump() { dbgFile << "########## KisFilterChain with" << m_chainLinks.count() << " members:"; ChainLink* link = m_chainLinks.first(); while (link) { link->dump(); link = m_chainLinks.next(); } dbgFile << "########## KisFilterChain (done) ##########"; } void KisFilterChain::appendChainLink(KisFilterEntrySP filterEntry, const QByteArray& from, const QByteArray& to) { m_chainLinks.append(new ChainLink(this, filterEntry, from, to)); } void KisFilterChain::prependChainLink(KisFilterEntrySP filterEntry, const QByteArray& from, const QByteArray& to) { m_chainLinks.prepend(new ChainLink(this, filterEntry, from, to)); } QString KisFilterChain::filterManagerImportFile() const { return m_manager->importFile(); } QString KisFilterChain::filterManagerExportFile() const { return m_manager->exportFile(); } KisDocument* KisFilterChain::filterManagerKisDocument() const { return m_manager->document(); } int KisFilterChain::filterManagerDirection() const { return m_manager->direction(); } KisFilterChain* KisFilterChain::filterManagerParentChain() const { return m_manager->parentChain(); } void KisFilterChain::manageIO() { m_inputQueried = Nil; m_outputQueried = Nil; delete m_inputStorageDevice; m_inputStorageDevice = 0; if (m_inputStorage) { m_inputStorage->close(); delete m_inputStorage; m_inputStorage = 0; } delete m_inputTempFile; // autodelete m_inputTempFile = 0; m_inputFile.clear(); if (!m_outputFile.isEmpty()) { if (m_outputTempFile == 0) { m_inputTempFile = new QTemporaryFile; m_inputTempFile->setAutoRemove(true); m_inputTempFile->setFileName(m_outputFile); } else { m_inputTempFile = m_outputTempFile; m_outputTempFile = 0; } m_inputFile = m_outputFile; m_outputFile.clear(); m_inputTempFile = m_outputTempFile; m_outputTempFile = 0; delete m_outputStorageDevice; m_outputStorageDevice = 0; if (m_outputStorage) { m_outputStorage->close(); // Don't delete the storage if we're just pointing to the // storage of the parent filter chain if (!filterManagerParentChain() || m_outputStorage->mode() != KoStore::Write) delete m_outputStorage; m_outputStorage = 0; } } if (m_inputDocument != filterManagerKisDocument()) delete m_inputDocument; m_inputDocument = m_outputDocument; m_outputDocument = 0; } void KisFilterChain::finalizeIO() { // In case we export (to a file, of course) and the last // filter chose to output a KisDocument we have to save it. // Should be very rare, but well... // Note: m_*input*Document as we already called manageIO() if (m_inputDocument && static_cast(filterManagerDirection()) == KisImportExportManager::Export) { dbgFile << "Saving the output document to the export file " << m_chainLinks.current()->to(); m_inputDocument->setOutputMimeType(m_chainLinks.current()->to()); m_inputDocument->saveNativeFormat(filterManagerExportFile()); m_inputFile = filterManagerExportFile(); } } bool KisFilterChain::createTempFile(QTemporaryFile** tempFile, bool autoDelete) { if (*tempFile) { errFile << "Ooops, why is there already a temp file???" << endl; return false; } *tempFile = new QTemporaryFile(); (*tempFile)->setAutoRemove(autoDelete); return (*tempFile)->open(); } /* Note about Windows & usage of QTemporaryFile The QTemporaryFile objects m_inputTempFile and m_outputTempFile are just used to reserve a temporary file with a unique name which then can be used to store an intermediate format. The filters themselves do not get access to these objects, but can query KisFilterChain only for the filename and then have to open the files themselves with their own file handlers (TODO: change this). On Windows this seems to be a problem and results in content not sync'ed to disk etc. So unless someone finds out which flags might be needed on opening the files on Windows to prevent this behaviour (unless these details are hidden away by the Qt abstraction and cannot be influenced), a workaround is to destruct the QTemporaryFile objects right after creation again and just take the name, to avoid having two file handlers on the same file. A better fix might be to use the QTemporaryFile objects also by the filters, instead of having them open the same file on their own again, but that needs more work and is left for... you :) */ void KisFilterChain::inputFileHelper(KisDocument* document, const QString& alternativeFile) { if (document) { if (!createTempFile(&m_inputTempFile)) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile.clear(); return; } m_inputFile = m_inputTempFile->fileName(); // See "Note about Windows & usage of QTemporaryFile" above #ifdef Q_OS_WIN m_inputTempFile->close(); m_inputTempFile->setAutoRemove(true); delete m_inputTempFile; m_inputTempFile = 0; #endif document->setOutputMimeType(m_chainLinks.current()->from()); if (!document->saveNativeFormat(m_inputFile)) { delete m_inputTempFile; m_inputTempFile = 0; m_inputFile.clear(); return; } } else m_inputFile = alternativeFile; } void KisFilterChain::outputFileHelper(bool autoDelete) { if (!createTempFile(&m_outputTempFile, autoDelete)) { delete m_outputTempFile; m_outputTempFile = 0; m_outputFile.clear(); } else { m_outputFile = m_outputTempFile->fileName(); // See "Note about Windows & usage of QTemporaryFile" above #ifdef Q_OS_WIN m_outputTempFile->close(); m_outputTempFile->setAutoRemove(true); delete m_outputTempFile; m_outputTempFile = 0; #endif } } KoStoreDevice* KisFilterChain::storageNewStreamHelper(KoStore** storage, KoStoreDevice** device, const QString& name) { delete *device; *device = 0; if ((*storage)->isOpen()) (*storage)->close(); if ((*storage)->bad()) return storageCleanupHelper(storage); if (!(*storage)->open(name)) return 0; *device = new KoStoreDevice(*storage); return *device; } KoStoreDevice* KisFilterChain::storageHelper(const QString& file, const QString& streamName, KoStore::Mode mode, KoStore** storage, KoStoreDevice** device) { if (file.isEmpty()) return 0; if (*storage) { dbgFile << "Uh-oh, we forgot to clean up..."; return 0; } storageInit(file, mode, storage); if ((*storage)->bad()) return storageCleanupHelper(storage); // Seems that we got a valid storage, at least. Even if we can't open // the stream the "user" asked us to open, we nonetheless change the // IOState from File to Storage, as it might be possible to open other streams if (mode == KoStore::Read) m_inputQueried = Storage; else // KoStore::Write m_outputQueried = Storage; return storageCreateFirstStream(streamName, storage, device); } void KisFilterChain::storageInit(const QString& file, KoStore::Mode mode, KoStore** storage) { QByteArray appIdentification(""); if (mode == KoStore::Write) { // To create valid storages we also have to add the mimetype // magic "applicationIndentifier" to the storage. // As only filters with a Calligra destination should query // for a storage to write to, we don't check the content of // the mimetype here. It doesn't do a lot of harm if someome // "abuses" this method. appIdentification = m_chainLinks.current()->to(); } *storage = KoStore::createStore(file, mode, appIdentification); } KoStoreDevice* KisFilterChain::storageCreateFirstStream(const QString& streamName, KoStore** storage, KoStoreDevice** device) { if (!(*storage)->open(streamName)) return 0; if (*device) { dbgFile << "Uh-oh, we forgot to clean up the storage device!"; (*storage)->close(); return storageCleanupHelper(storage); } *device = new KoStoreDevice(*storage); return *device; } KoStoreDevice* KisFilterChain::storageCleanupHelper(KoStore** storage) { // Take care not to delete the storage of the parent chain if (*storage != m_outputStorage || !filterManagerParentChain() || (*storage)->mode() != KoStore::Write) delete *storage; *storage = 0; return 0; } KisDocument* KisFilterChain::createDocument(const QString& file) { QUrl url = QUrl::fromLocalFile(file); QMimeDatabase db; QMimeType t = db.mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension); if (t.isDefault()) { errFile << "No mimetype found for " << file << endl; return 0; } KisDocument *doc = createDocument(t.name().toLatin1()); if (!doc || !doc->loadNativeFormat(file)) { errFile << "Couldn't load from the file" << endl; delete doc; return 0; } return doc; } KisDocument* KisFilterChain::createDocument(const QByteArray& mimeType) { Q_UNUSED(mimeType); return KisPart::instance()->createDocument(); } int KisFilterChain::weight() const { return m_chainLinks.count(); } diff --git a/krita/ui/KisFilterChainLink.h b/krita/ui/KisFilterChainLink.h index daf9e07cd0..673b3b2a70 100644 --- a/krita/ui/KisFilterChainLink.h +++ b/krita/ui/KisFilterChainLink.h @@ -1,78 +1,77 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISFILTERCHAINLINK_H #define KISFILTERCHAINLINK_H #include class QByteArray; -class KoUpdater; namespace CalligraFilter { /** * A small private helper class with represents one single filter * (one link of the chain) * @internal */ class ChainLink { public: ChainLink(KisFilterChain *chain, KisFilterEntrySP filterEntry, const QByteArray& from, const QByteArray& to); ~ChainLink(); KisImportExportFilter::ConversionStatus invokeFilter(const ChainLink * const parentChainLink); QByteArray from() const { return m_from; } QByteArray to() const { return m_to; } // debugging void dump() const; QPointer updater() const { return m_updater; } private: ChainLink(const ChainLink& rhs); ChainLink& operator=(const ChainLink& rhs); void setupCommunication(const KisImportExportFilter * const parentFilter) const; void setupConnections(const KisImportExportFilter *sender, const KisImportExportFilter *receiver) const; KisFilterChain *m_chain; KisFilterEntrySP m_filterEntry; QByteArray m_from, m_to; // This hack is only needed due to crappy Microsoft design and // circular dependencies in their embedded files :} KisImportExportFilter *m_filter; QPointer const m_updater; }; } #endif // KOFILTERCHAINLINK_H diff --git a/krita/ui/KisFilterGraph.h b/krita/ui/KisFilterGraph.h index a95b8d1c8b..e53477fd9d 100644 --- a/krita/ui/KisFilterGraph.h +++ b/krita/ui/KisFilterGraph.h @@ -1,79 +1,78 @@ /* This file is part of the Calligra libraries Copyright (C) 2001 Werner Trobin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_FILTERGRAPH_H #define KIS_FILTERGRAPH_H #include "kritaui_export.h" #include "KisFilterChain.h" -#include "KisFilterVertex.h" #include #include namespace CalligraFilter { /** * The main worker behind the scenes. Manages the creation of the graph, * processing the information in it, and creating the filter chains. * @internal * Only exported for unit tests. */ class KRITAUI_EXPORT Graph { public: explicit Graph(const QByteArray& from); ~Graph(); bool isValid() const { return m_graphValid; } QByteArray sourceMimeType() const { return m_from; } void setSourceMimeType(const QByteArray& from); // Creates a chain from "from" to the "to" mimetype // If the "to" mimetype isEmpty() then we try to find the // closest Calligra mimetype and use that as destination. // After such a search "to" will contain the dest. mimetype (return value) // if the search was successful. Might return 0! KisFilterChainSP chain(const KisImportExportManager* manager, QByteArray& to) const; // debugging void dump() const; private: Graph(const Graph& rhs); Graph& operator=(const Graph& rhs); void buildGraph(); void shortestPaths(); QByteArray findCalligraPart() const; QHash m_vertices; QByteArray m_from; bool m_graphValid; class Private; Private * const d; }; } #endif // KOFILTERGRAPH_H diff --git a/krita/ui/KisImportExportManager.h b/krita/ui/KisImportExportManager.h index cee7c5eea0..077d0513ee 100644 --- a/krita/ui/KisImportExportManager.h +++ b/krita/ui/KisImportExportManager.h @@ -1,208 +1,208 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis 2000, 2001 Werner Trobin Copyright (C) 2004 Nicolas Goutte This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_IMPORT_EXPORT_MANAGER_H #define KIS_IMPORT_EXPORT_MANAGER_H #include #include #include #include -#include "KisFilterChain.h" #include "KisFilterGraph.h" #include "kritaui_export.h" +class KisFilterChain; class KisDocument; class KoProgressUpdater; /** * @brief The class managing all the filters. * * This class manages all filters for a %Calligra application. Normally * you will not have to use it, since KisMainWindow takes care of loading * and saving documents. * * @ref KisFilter * * @author Kalle Dalheimer * @author Torben Weis * @author Werner Trobin */ class KRITAUI_EXPORT KisImportExportManager : public QObject { Q_OBJECT public: /** * This enum is used to distinguish the import/export cases */ enum Direction { Import = 1, Export = 2 }; /** * Create a filter manager for a document */ explicit KisImportExportManager(KisDocument *document, KoProgressUpdater *progressUpdater = 0); /** * Create a filter manager for the Shape Collection docker. * @param mimeType the mimetype to import to. */ explicit KisImportExportManager(const QByteArray& mimeType); /** * Create a filter manager for a filter which wants to embed something. * The path it passes is the file to convert. You cannot use * the @ref importDocument() method -- use @ref exportDocument() to convert * the file to the destination mimetype you prefer. * * @param path The location you want to export * @param mimetypeHint The mimetype of the file you want to export. You have * to specify this information only if the automatic detection will * fail because e.g. you saved an embedded stream to a *.tmp file. * Most likely you do not have to care about that. * @param parentChain The parent filter chain of this filter manager. Used * to allow embedding for filters. Most likely you do not have to care. */ explicit KisImportExportManager(const QString& location, const QByteArray& mimetypeHint = QByteArray(), KisFilterChain * const parentChain = 0); virtual ~KisImportExportManager(); /** * Imports the specified document and returns the resultant filename * (most likely some file in /tmp). * @p path can be either a URL or a filename. * @p documentMimeType gives importDocument a hint about what type * the document may be. It can be left empty. * @p status signals the success/error of the conversion. * If the QString which is returned isEmpty() and the status is OK, * then we imported the file directly into the document. */ QString importDocument(const QString& location, const QString& documentMimeType, KisImportExportFilter::ConversionStatus& status); /** * @brief Exports the given file/document to the specified URL/mimetype. * * If @p mimeType is empty, then the closest matching Calligra part is searched * and when the method returns @p mimeType contains this mimetype. * Oh, well, export is a C++ keyword ;) */ KisImportExportFilter::ConversionStatus exportDocument(const QString& location, QByteArray& mimeType); ///@name Static API //@{ /** * Suitable for passing to KoFileDialog::setMimeTypeFilters. The default mime * gets set by the "users" of this method, as we do not have enough * information here. * Optionally, @p extraNativeMimeTypes are added after the native mimetype. */ static QStringList mimeFilter(const QByteArray& mimetype, Direction direction, const QStringList& extraNativeMimeTypes = QStringList()); /** * The same method as KisFilterManager::mimeFilter but suited for KoShell. * We do not need the mimetype, as we will simply use all available * %Calligra mimetypes. The Direction enum is omitted, as we only * call this for importing. When saving from KoShell we already * know the Calligra part we are using. */ static QStringList mimeFilter(); /** * Method used to check if that filter is available at all. * @note Slow, but cached */ static bool filterAvailable(KisFilterEntrySP entry); //@} /** * Set the filter manager is batch mode (no dialog shown) * instead of the interactive mode (dialog shown) */ void setBatchMode(const bool batch); /** * Get if the filter manager is batch mode (true) * or in interactive mode (true) */ bool getBatchMode(void) const; /** * Return the KoProgressUpdater or NULL if there is none. **/ KoProgressUpdater *progressUpdater() const; private: // === API for KisFilterChains === (internal) // The friend methods are private in KisFilterChain and // just forward calls to the methods here. Should be // pretty safe. friend QString KisFilterChain::filterManagerImportFile() const; QString importFile() const { return m_importUrl.toLocalFile(); } friend QString KisFilterChain::filterManagerExportFile() const; QString exportFile() const { return m_exportUrl.toLocalFile(); } friend KisDocument *KisFilterChain::filterManagerKisDocument() const; KisDocument *document() const { return m_document; } friend int KisFilterChain::filterManagerDirection() const; int direction() const { return static_cast(m_direction); } friend KisFilterChain *KisFilterChain::filterManagerParentChain() const; KisFilterChain *parentChain() const { return m_parentChain; } // Private API KisImportExportManager(const KisImportExportManager& rhs); KisImportExportManager &operator=(const KisImportExportManager& rhs); // Convert file path string or URL string into QUrl QUrl locationToUrl(QString location) const; void importErrorHelper(const QString& mimeType, const bool suppressDialog = false); KisDocument *m_document; KisFilterChain *const m_parentChain; QUrl m_importUrl; QUrl m_exportUrl; QByteArray m_importUrlMimetypeHint; ///< suggested mimetype CalligraFilter::Graph m_graph; Direction m_direction; /// A static cache for the availability checks of filters static QMap m_filterAvailable; class Private; Private * const d; }; #endif // __KO_FILTER_MANAGER_H__ diff --git a/krita/ui/KisMainWindow.cpp b/krita/ui/KisMainWindow.cpp index 00f6ac5b24..95995b0004 100644 --- a/krita/ui/KisMainWindow.cpp +++ b/krita/ui/KisMainWindow.cpp @@ -1,2223 +1,2222 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2006 David Faure Copyright (C) 2007, 2009 Thomas zander Copyright (C) 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisMainWindow.h" // qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoDockFactoryBase.h" #include "KoDockWidgetTitleBar.h" #include "KoDocumentInfoDlg.h" #include "KoDocumentInfo.h" #include "KoFileDialog.h" #include #include #include #include #include #include "KoToolDocker.h" #include #include #include #include #include "KisView.h" #include "KisDocument.h" #include "KisImportExportManager.h" #include "KisPrintJob.h" #include "KisPart.h" #include "KisApplication.h" #include "kis_action.h" #include "kis_canvas_controller.h" #include "kis_canvas2.h" #include "KisViewManager.h" #include "KisDocument.h" -#include "KisView.h" #include "dialogs/kis_dlg_preferences.h" #include "kis_config_notifier.h" #include "kis_canvas_resource_provider.h" #include "kis_node.h" #include "kis_image.h" #include "kis_group_layer.h" #include "kis_paintop_settings.h" #include "kis_paintop_box.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "dialogs/kis_about_application.h" #include "kis_mainwindow_observer.h" #include "kis_action_manager.h" #include "thememanager.h" #include "kis_resource_server_provider.h" #include "kis_icon_utils.h" #include #include class ToolDockerFactory : public KoDockFactoryBase { public: ToolDockerFactory() : KoDockFactoryBase() { } QString id() const { return "sharedtooldocker"; } QDockWidget* createDockWidget() { KoToolDocker* dockWidget = new KoToolDocker(); dockWidget->setTabEnabled(false); return dockWidget; } DockPosition defaultDockPosition() const { return DockRight; } }; class Q_DECL_HIDDEN KisMainWindow::Private { public: Private(KisMainWindow *parent) : viewManager(0) , firstTime(true) , windowSizeDirty(false) , readOnly(false) , isImporting(false) , isExporting(false) , noCleanup(false) , showDocumentInfo(0) , saveAction(0) , saveActionAs(0) , printAction(0) , printActionPreview(0) , exportPdf(0) , closeAll(0) // , reloadFile(0) , importFile(0) , exportFile(0) , undo(0) , redo(0) , newWindow(0) , close(0) , mdiCascade(0) , mdiTile(0) , mdiNextWindow(0) , mdiPreviousWindow(0) , toggleDockers(0) , toggleDockerTitleBars(0) , dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent)) , windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent)) , documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent)) , helpMenu(0) , brushesAndStuff(0) , recentFiles(0) , toolOptionsDocker(0) , deferredClosingEvent(0) , themeManager(0) , mdiArea(new QMdiArea(parent)) , activeSubWindow(0) , windowMapper(new QSignalMapper(parent)) , documentMapper(new QSignalMapper(parent)) , lastExportSpecialOutputFlag(0) { } ~Private() { qDeleteAll(toolbarList); } KisViewManager *viewManager; QPointer activeView; QPointer progress; QMutex progressMutex; QList toolbarList; bool firstTime; bool windowSizeDirty; bool readOnly; bool isImporting; bool isExporting; bool noCleanup; KisAction *showDocumentInfo; KisAction *saveAction; KisAction *saveActionAs; KisAction *printAction; KisAction *printActionPreview; KisAction *exportPdf; KisAction *closeAll; // KisAction *reloadFile; KisAction *importFile; KisAction *exportFile; KisAction *undo; KisAction *redo; KisAction *newWindow; KisAction *close; KisAction *mdiCascade; KisAction *mdiTile; KisAction *mdiNextWindow; KisAction *mdiPreviousWindow; KisAction *toggleDockers; KisAction *toggleDockerTitleBars; KisAction *expandingSpacers[2]; KActionMenu *dockWidgetMenu; KActionMenu *windowMenu; KActionMenu *documentMenu; KHelpMenu *helpMenu; KToolBar *brushesAndStuff; KRecentFilesAction *recentFiles; QUrl lastExportUrl; QMap dockWidgetsMap; QMap dockWidgetVisibilityMap; QByteArray dockerStateBeforeHiding; KoToolDocker *toolOptionsDocker; QCloseEvent *deferredClosingEvent; Digikam::ThemeManager *themeManager; QMdiArea *mdiArea; QMdiSubWindow *activeSubWindow; QSignalMapper *windowMapper; QSignalMapper *documentMapper; QByteArray lastExportedFormat; int lastExportSpecialOutputFlag; }; KisMainWindow::KisMainWindow() : KXmlGuiWindow() , d(new Private(this)) { KisConfig cfg; d->viewManager = new KisViewManager(this, actionCollection()); d->themeManager = new Digikam::ThemeManager(this); setAcceptDrops(true); setStandardToolBarMenuEnabled(true); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); setDockNestingEnabled(true); qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events #ifdef Q_OS_MAC setUnifiedTitleAndToolBarOnMac(true); #endif connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts())); connect(this, SIGNAL(documentSaved()), d->viewManager, SLOT(slotDocumentSaved())); connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons())); connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu())); connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu())); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged())); actionCollection()->addAssociatedWidget(this); QMetaObject::invokeMethod(this, "initializeGeometry", Qt::QueuedConnection); KoToolBoxFactory toolBoxFactory; QDockWidget *toolbox = createDockWidget(&toolBoxFactory); toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable); if (cfg.toolOptionsInDocker()) { ToolDockerFactory toolDockerFactory; d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory)); } QMap dockwidgetActions; dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction(); foreach(const QString & docker, KoDockRegistry::instance()->keys()) { KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker); QDockWidget *dw = createDockWidget(factory); dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction(); } foreach(QString title, dockwidgetActions.keys()) { d->dockWidgetMenu->addAction(dockwidgetActions[title]); } foreach (QDockWidget *wdg, dockWidgets()) { if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) { wdg->setVisible(true); } } foreach(KoCanvasObserverBase* observer, canvasObservers()) { observer->setObservedCanvas(0); KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer); if (mainwindowObserver) { mainwindowObserver->setMainWindow(d->viewManager); } } d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); d->mdiArea->setTabPosition(QTabWidget::North); d->mdiArea->setTabsClosable(true); setCentralWidget(d->mdiArea); connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated())); connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*))); createActions(); setAutoSaveSettings("krita", false); KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), viewManager()); subWindowActivated(); updateWindowMenu(); if (isHelpMenuEnabled() && !d->helpMenu) { d->helpMenu = new KHelpMenu(this, "Dummy Text That Is Not Used In Frameworks 5", false); KActionCollection *actions = actionCollection(); QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents); QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis); QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug); QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage); QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp); QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE); if (helpContentsAction) { actions->addAction(helpContentsAction->objectName(), helpContentsAction); } if (whatsThisAction) { actions->addAction(whatsThisAction->objectName(), whatsThisAction); } if (reportBugAction) { actions->addAction(reportBugAction->objectName(), reportBugAction); } if (switchLanguageAction) { actions->addAction(switchLanguageAction->objectName(), switchLanguageAction); } if (aboutAppAction) { actions->addAction(aboutAppAction->objectName(), aboutAppAction); } if (aboutKdeAction) { actions->addAction(aboutKdeAction->objectName(), aboutKdeAction); } connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication())); } // KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves QAction *helpAction = actionCollection()->action("help_contents"); helpAction->disconnect(); connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual())); #if 0 //check for colliding shortcuts QSet existingShortcuts; foreach(QAction* action, actionCollection()->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } dbgKrita << "shortcut " << action->text() << " " << action->shortcut(); Q_ASSERT(!existingShortcuts.contains(action->shortcut())); existingShortcuts.insert(action->shortcut()); } #endif configChanged(); QString doc; QStringList allFiles = KoResourcePaths::findAllResources("data", "krita/krita.rc"); KIS_ASSERT(allFiles.size() > 0); // We need at least one krita.rc file! setXMLFile(findMostRecentXMLFile(allFiles, doc)); setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita/krita.rc")); guiFactory()->addClient(this); // Create and plug toolbar list for Settings menu QList toolbarList; foreach(QWidget* it, guiFactory()->containers("ToolBar")) { KToolBar * toolBar = ::qobject_cast(it); if (toolBar) { if (toolBar->objectName() == "BrushesAndStuff") { toolBar->setEnabled(false); } KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this); actionCollection()->addAction(toolBar->objectName().toUtf8(), act); act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle()))); connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool))); act->setChecked(!toolBar->isHidden()); toolbarList.append(act); } else warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!"; } plugActionList("toolbarlist", toolbarList); setToolbarList(toolbarList); applyToolBarLayout(); d->viewManager->updateGUI(); d->viewManager->updateIcons(); QTimer::singleShot(1000, this, SLOT(checkSanity())); } void KisMainWindow::setNoCleanup(bool noCleanup) { d->noCleanup = noCleanup; } KisMainWindow::~KisMainWindow() { // foreach(QAction *ac, actionCollection()->actions()) { // QAction *action = qobject_cast(ac); // if (action) { // dbgKrita << "", "").replace("", "") // << "iconText=" << action->iconText().replace("&", "&") // << "shortcut=" << action->shortcut(QAction::ActiveShortcut).toString() // << "defaultShortcut=" << action->shortcut(QAction::DefaultShortcut).toString() // << "isCheckable=" << QString((action->isChecked() ? "true" : "false")) // << "statusTip=" << action->statusTip() // << "/>" ; // } // else { // dbgKrita << "Got a QAction:" << ac->objectName(); // } // } KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); cfg.writeEntry("ko_geometry", saveGeometry().toBase64()); cfg.writeEntry("ko_windowstate", saveState().toBase64()); { KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); } // The doc and view might still exist (this is the case when closing the window) KisPart::instance()->removeMainWindow(this); if (d->noCleanup) return; delete d->viewManager; delete d; } void KisMainWindow::addView(KisView *view) { //dbgKrita << "KisMainWindow::addView" << view; if (d->activeView == view) return; if (d->activeView) { d->activeView->disconnect(this); } showView(view); updateCaption(); emit restoringDone(); if (d->activeView) { connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified(QString,bool))); } } void KisMainWindow::showView(KisView *imageView) { if (imageView && activeView() != imageView) { // XXX: find a better way to initialize this! imageView->setViewManager(d->viewManager); imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager()); imageView->slotLoadingFinished(); QMdiSubWindow *subwin = d->mdiArea->addSubWindow(imageView); subwin->setAttribute(Qt::WA_DeleteOnClose, true); connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu())); KisConfig cfg; subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setWindowIcon(qApp->windowIcon()); if (d->mdiArea->subWindowList().size() == 1) { imageView->showMaximized(); } else { imageView->show(); } setActiveView(imageView); updateWindowMenu(); updateCaption(); } } void KisMainWindow::slotPreferences() { if (KisDlgPreferences::editPreferences()) { KisConfigNotifier::instance()->notifyConfigChanged(); // XXX: should this be changed for the views in other windows as well? foreach(QPointer koview, KisPart::instance()->views()) { KisViewManager *view = qobject_cast(koview); if (view) { // Update the settings for all nodes -- they don't query // KisConfig directly because they need the settings during // compositing, and they don't connect to the config notifier // because nodes are not QObjects (because only one base class // can be a QObject). KisNode* node = dynamic_cast(view->image()->rootLayer().data()); node->updateSettings(); } } d->viewManager->showHideScrollbars(); } } void KisMainWindow::slotThemeChanged() { // save theme changes instantly KConfigGroup group( KSharedConfig::openConfig(), "theme"); group.writeEntry("Theme", d->themeManager->currentThemeName()); // reload action icons! foreach (QAction *action, actionCollection()->actions()) { KisIconUtils::updateIcon(action); } emit themeChanged(); } void KisMainWindow::updateReloadFileAction(KisDocument *doc) { Q_UNUSED(doc); // d->reloadFile->setEnabled(doc && !doc->url().isEmpty()); } void KisMainWindow::setReadWrite(bool readwrite) { d->saveAction->setEnabled(readwrite); d->importFile->setEnabled(readwrite); d->readOnly = !readwrite; updateCaption(); } void KisMainWindow::addRecentURL(const QUrl &url) { dbgUI << "KisMainWindow::addRecentURL url=" << url.toDisplayString(); // Add entry to recent documents list // (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.) if (!url.isEmpty()) { bool ok = true; if (url.isLocalFile()) { QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp"); for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) if (path.contains(*it)) ok = false; // it's in the tmp resource if (ok) { KRecentDocument::add(path); KRecentDirs::add(":OpenDialog", QFileInfo(path).dir().canonicalPath()); } } else { KRecentDocument::add(url.url(QUrl::StripTrailingSlash), true); } if (ok) { d->recentFiles->addUrl(url); } saveRecentFiles(); } } void KisMainWindow::saveRecentFiles() { // Save list of recent files KSharedConfigPtr config = KSharedConfig::openConfig(); d->recentFiles->saveEntries(config->group("RecentFiles")); config->sync(); // Tell all windows to reload their list, after saving // Doesn't work multi-process, but it's a start foreach(KMainWindow* window, KMainWindow::memberList()) static_cast(window)->reloadRecentFileList(); } void KisMainWindow::reloadRecentFileList() { d->recentFiles->loadEntries( KSharedConfig::openConfig()->group("RecentFiles")); } void KisMainWindow::updateCaption() { if (!d->mdiArea->activeSubWindow()) { updateCaption(QString(), false); } else { QString caption( d->activeView->document()->caption() ); if (d->readOnly) { caption += ' ' + i18n("(write protected)"); } d->activeView->setWindowTitle(caption); updateCaption(caption, d->activeView->document()->isModified()); if (!d->activeView->document()->url().fileName().isEmpty()) d->saveAction->setToolTip(i18n("Save as %1", d->activeView->document()->url().fileName())); else d->saveAction->setToolTip(i18n("Save")); } } void KisMainWindow::updateCaption(const QString & caption, bool mod) { dbgUI << "KisMainWindow::updateCaption(" << caption << "," << mod << ")"; #ifdef CALLIGRA_ALPHA setCaption(QString("ALPHA %1: %2").arg(CALLIGRA_ALPHA).arg(caption), mod); return; #endif #ifdef CALLIGRA_BETA setCaption(QString("BETA %1: %2").arg(CALLIGRA_BETA).arg(caption), mod); return; #endif #ifdef CALLIGRA_RC setCaption(QString("RELEASE CANDIDATE %1: %2").arg(CALLIGRA_RC).arg(caption), mod); return; #endif setCaption(caption, mod); } KisView *KisMainWindow::activeView() const { if (d->activeView) { return d->activeView; } return 0; } bool KisMainWindow::openDocument(const QUrl &url) { if (!QFile(url.toLocalFile()).exists()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url())); d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list saveRecentFiles(); return false; } return openDocumentInternal(url); } bool KisMainWindow::openDocumentInternal(const QUrl &url, KisDocument *newdoc) { if (!newdoc) { newdoc = KisPart::instance()->createDocument(); } d->firstTime = true; connect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); connect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); bool openRet = (!isImporting()) ? newdoc->openUrl(url) : newdoc->importDocument(url); if (!openRet) { delete newdoc; return false; } KisPart::instance()->addDocument(newdoc); updateReloadFileAction(newdoc); KFileItem file(url, newdoc->mimeType(), KFileItem::Unknown); if (!file.isWritable()) { setReadWrite(false); } return true; } // Separate from openDocument to handle async loading (remote URLs) void KisMainWindow::slotLoadCompleted() { KisDocument *newdoc = qobject_cast(sender()); KisView *view = KisPart::instance()->createView(newdoc, resourceManager(), actionCollection(), this); addView(view); disconnect(newdoc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(newdoc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); emit loadCompleted(); } void KisMainWindow::slotLoadCanceled(const QString & errMsg) { dbgUI << "KisMainWindow::slotLoadCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); // ... can't delete the document, it's the one who emitted the signal... KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotLoadCanceled(const QString &))); } void KisMainWindow::slotSaveCanceled(const QString &errMsg) { dbgUI << "KisMainWindow::slotSaveCanceled"; if (!errMsg.isEmpty()) // empty when canceled by user QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg); slotSaveCompleted(); } void KisMainWindow::slotSaveCompleted() { dbgUI << "KisMainWindow::slotSaveCompleted"; KisDocument* doc = qobject_cast(sender()); Q_ASSERT(doc); disconnect(doc, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); disconnect(doc, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); if (d->deferredClosingEvent) { KXmlGuiWindow::closeEvent(d->deferredClosingEvent); } } bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool silent, int specialOutputFlag) { if (!document) { return true; } bool reset_url; if (document->url().isEmpty()) { reset_url = true; saveas = true; } else { reset_url = false; } connect(document, SIGNAL(sigProgress(int)), this, SLOT(slotProgress(int))); connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted())); connect(document, SIGNAL(canceled(const QString &)), this, SLOT(slotSaveCanceled(const QString &))); QUrl oldURL = document->url(); QString oldFile = document->localFilePath(); QByteArray _native_format = document->nativeFormatMimeType(); QByteArray oldOutputFormat = document->outputMimeType(); int oldSpecialOutputFlag = document->specialOutputFlag(); QUrl suggestedURL = document->url(); QStringList mimeFilter; QMimeDatabase db; QMimeType mime = db.mimeTypeForName(_native_format); if (specialOutputFlag) { mimeFilter = mime.globPatterns(); } else { mimeFilter = KisImportExportManager::mimeFilter(_native_format, KisImportExportManager::Export, document->extraNativeMimeTypes()); } if (!mimeFilter.contains(oldOutputFormat) && !isExporting()) { dbgUI << "KisMainWindow::saveDocument no export filter for" << oldOutputFormat; // --- don't setOutputMimeType in case the user cancels the Save As // dialog and then tries to just plain Save --- // suggest a different filename extension (yes, we fortunately don't all live in a world of magic :)) QString suggestedFilename = suggestedURL.fileName(); if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name int c = suggestedFilename.lastIndexOf('.'); const QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (c < 0) suggestedFilename += ext; else suggestedFilename = suggestedFilename.left(c) + ext; } else { // current filename extension wrong anyway if (c > 0) { // this assumes that a . signifies an extension, not just a . suggestedFilename = suggestedFilename.left(c); } } suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename); suggestedURL.setPath(suggestedURL.path() + suggestedFilename); } // force the user to choose outputMimeType saveas = true; } bool ret = false; if (document->url().isEmpty() || saveas) { // if you're just File/Save As'ing to change filter options you // don't want to be reminded about overwriting files etc. bool justChangingFilterOptions = false; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("untitled")); if (isExporting() && !d->lastExportUrl.isEmpty()) { dialog.setDefaultDir(d->lastExportUrl.toLocalFile(), true); } else { dialog.setDefaultDir(suggestedURL.toLocalFile(), true); } dialog.setMimeTypeFilters(mimeFilter, KIS_MIME_TYPE); QUrl newURL = QUrl::fromUserInput(dialog.filename()); if (newURL.isLocalFile()) { QString fn = newURL.toLocalFile(); if (QFileInfo(fn).completeSuffix().isEmpty()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForName(_native_format); fn.append(mime.preferredSuffix()); newURL = QUrl::fromLocalFile(fn); } } if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) { QString fn = newURL.toLocalFile(); QFileInfo info(fn); document->documentInfo()->setAboutInfo("title", info.baseName()); } QByteArray outputFormat = _native_format; if (!specialOutputFlag) { QMimeType mime = db.mimeTypeForUrl(newURL); QString outputFormatString = mime.name(); outputFormat = outputFormatString.toLatin1(); } if (!isExporting()) justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType()) && (specialOutputFlag == oldSpecialOutputFlag); else justChangingFilterOptions = (newURL == d->lastExportUrl) && (outputFormat == d->lastExportedFormat) && (specialOutputFlag == d->lastExportSpecialOutputFlag); bool bOk = true; if (newURL.isEmpty()) { bOk = false; } // adjust URL before doing checks on whether the file exists. if (specialOutputFlag) { QString fileName = newURL.fileName(); if ( specialOutputFlag== KisDocument::SaveAsDirectoryStore) { //dbgKrita << "save to directory: " << newURL.url(); } else if (specialOutputFlag == KisDocument::SaveEncrypted) { int dot = fileName.lastIndexOf('.'); dbgKrita << dot; QString ext = mime.preferredSuffix(); if (!ext.isEmpty()) { if (dot < 0) fileName += ext; else fileName = fileName.left(dot) + ext; } else { // current filename extension wrong anyway if (dot > 0) fileName = fileName.left(dot); } newURL = newURL.adjusted(QUrl::RemoveFilename); newURL.setPath(newURL.path() + fileName); } } if (bOk) { bool wantToSave = true; // don't change this line unless you know what you're doing :) if (!justChangingFilterOptions || document->confirmNonNativeSave(isExporting())) { if (!document->isNativeFormat(outputFormat)) wantToSave = true; } if (wantToSave) { // // Note: // If the user is stupid enough to Export to the current URL, // we do _not_ change this operation into a Save As. Reasons // follow: // // 1. A check like "isExporting() && oldURL == newURL" // doesn't _always_ work on case-insensitive filesystems // and inconsistent behaviour is bad. // 2. It is probably not a good idea to change document->mimeType // and friends because the next time the user File/Save's, // (not Save As) they won't be expecting that they are // using their File/Export settings // // As a bad side-effect of this, the modified flag will not // be updated and it is possible that what is currently on // their screen is not what is stored on disk (through loss // of formatting). But if you are dumb enough to change // mimetype but not the filename, then arguably, _you_ are // the "bug" :) // // - Clarence // document->setOutputMimeType(outputFormat, specialOutputFlag); if (!isExporting()) { // Save As ret = document->saveAs(newURL); if (ret) { dbgUI << "Successful Save As!"; addRecentURL(newURL); setReadWrite(true); } else { dbgUI << "Failed Save As!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } } else { // Export ret = document->exportDocument(newURL); if (ret) { // a few file dialog convenience things d->lastExportUrl = newURL; d->lastExportedFormat = outputFormat; d->lastExportSpecialOutputFlag = specialOutputFlag; } // always restore output format document->setOutputMimeType(oldOutputFormat, oldSpecialOutputFlag); } if (silent) // don't let the document change the window caption document->setTitleModified(); } // if (wantToSave) { else ret = false; } // if (bOk) { else ret = false; } else { // saving bool needConfirm = document->confirmNonNativeSave(false) && !document->isNativeFormat(oldOutputFormat); if (!needConfirm || (needConfirm && exportConfirmation(oldOutputFormat /* not so old :) */)) ) { // be sure document has the correct outputMimeType! if (isExporting() || document->isModified()) { ret = document->save(); } if (!ret) { dbgUI << "Failed Save!"; document->setUrl(oldURL); document->setLocalFilePath(oldFile); } } else ret = false; } if (!ret && reset_url) document->resetURL(); //clean the suggested filename as the save dialog was rejected updateReloadFileAction(document); updateCaption(); return ret; } bool KisMainWindow::exportConfirmation(const QByteArray &/*outputFormat*/) { return true; } void KisMainWindow::undo() { if (activeView()) { activeView()->undoAction()->trigger(); d->undo->setText(activeView()->undoAction()->text()); } } void KisMainWindow::redo() { if (activeView()) { activeView()->redoAction()->trigger(); d->redo->setText(activeView()->redoAction()->text()); } } void KisMainWindow::closeEvent(QCloseEvent *e) { d->mdiArea->closeAllSubWindows(); if(d->activeView && d->activeView->document() && d->activeView->document()->isLoading()) { e->setAccepted(false); return; } QList childrenList = d->mdiArea->subWindowList(); if (childrenList.isEmpty()) { d->deferredClosingEvent = e; if (!d->dockerStateBeforeHiding.isEmpty()) { restoreState(d->dockerStateBeforeHiding); } statusBar()->setVisible(true); menuBar()->setVisible(true); saveWindowSettings(); if (d->noCleanup) return; foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { KisView *view = dynamic_cast(subwin); if (view) { KisPart::instance()->removeView(view); } } if (!d->dockWidgetVisibilityMap.isEmpty()) { // re-enable dockers for persistency foreach(QDockWidget* dockWidget, d->dockWidgetsMap) dockWidget->setVisible(d->dockWidgetVisibilityMap.value(dockWidget)); } } else { e->setAccepted(false); } } void KisMainWindow::saveWindowSettings() { KSharedConfigPtr config = KSharedConfig::openConfig(); if (d->windowSizeDirty ) { dbgUI << "KisMainWindow::saveWindowSettings"; KConfigGroup group = config->group("MainWindow"); saveWindowSize(group); config->sync(); d->windowSizeDirty = false; } if (!d->activeView || d->activeView->document()) { // Save toolbar position into the config file of the app, under the doc's component name KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); // Save collapsable state of dock widgets for (QMap::const_iterator i = d->dockWidgetsMap.constBegin(); i != d->dockWidgetsMap.constEnd(); ++i) { if (i.value()->widget()) { KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key()); dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden()); dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool()); dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value())); } } } KSharedConfig::openConfig()->sync(); resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down } void KisMainWindow::resizeEvent(QResizeEvent * e) { d->windowSizeDirty = true; KXmlGuiWindow::resizeEvent(e); } void KisMainWindow::setActiveView(KisView* view) { d->activeView = view; updateCaption(); actionCollection()->action("edit_undo")->setText(activeView()->undoAction()->text()); actionCollection()->action("edit_redo")->setText(activeView()->redoAction()->text()); } void KisMainWindow::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->accept(); } } void KisMainWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) { foreach(const QUrl &url, event->mimeData()->urls()) { openDocument(url); } } } void KisMainWindow::slotFileNew() { KisPart::instance()->showStartUpWidget(this, true /*Always show widget*/); } void KisMainWindow::slotFileOpen() { QStringList urls; KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument"); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation)); dialog.setMimeTypeFilters(KisImportExportManager::mimeFilter(KIS_MIME_TYPE, KisImportExportManager::Import, KisDocumentEntry::extraNativeMimeTypes())); QStringList filters = dialog.nameFilters(); filters << i18n("All files (*.*)"); dialog.setNameFilters(filters); dialog.setHideNameFilterDetailsOption(); dialog.setCaption(isImporting() ? i18n("Import Images") : i18n("Open Images")); urls = dialog.filenames(); if (urls.isEmpty()) return; foreach(const QString& url, urls) { if (!url.isEmpty()) { bool res = openDocument(QUrl::fromLocalFile(url)); if (!res) { warnKrita << "Loading" << url << "failed"; } } } } void KisMainWindow::slotFileOpenRecent(const QUrl & url) { // Create a copy, because the original QUrl in the map of recent files in // KRecentFilesAction may get deleted. (void) openDocument(QUrl(url)); } void KisMainWindow::slotFileSave() { if (saveDocument(d->activeView->document())) emit documentSaved(); } void KisMainWindow::slotFileSaveAs() { if (saveDocument(d->activeView->document(), true)) emit documentSaved(); } KoCanvasResourceManager *KisMainWindow::resourceManager() const { return d->viewManager->resourceProvider()->resourceManager(); } int KisMainWindow::viewCount() const { return d->mdiArea->subWindowList().size(); } bool KisMainWindow::restoreWorkspace(const QByteArray &state) { QByteArray oldState = saveState(); const bool showTitlebars = KisConfig().showDockerTitleBars(); // needed because otherwise the layout isn't correctly restored in some situations Q_FOREACH (QDockWidget *dock, dockWidgets()) { dock->hide(); dock->titleBarWidget()->setVisible(showTitlebars); } bool success = KXmlGuiWindow::restoreState(state); if (!success) { KXmlGuiWindow::restoreState(oldState); Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { dock->titleBarWidget()->setVisible(showTitlebars || dock->isFloating()); } } return false; } Q_FOREACH (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(showTitlebars || (dock->isFloating() && isCollapsed)); } } return success; } KisViewManager *KisMainWindow::viewManager() const { return d->viewManager; } void KisMainWindow::slotDocumentInfo() { if (!d->activeView->document()) return; KoDocumentInfo *docInfo = d->activeView->document()->documentInfo(); if (!docInfo) return; KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo); if (dlg->exec()) { if (dlg->isDocumentSaved()) { d->activeView->document()->setModified(false); } else { d->activeView->document()->setModified(true); } d->activeView->document()->setTitleModified(); } delete dlg; } bool KisMainWindow::slotFileCloseAll() { foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { if (subwin) { if(!subwin->close()) return false; } } updateCaption(); return true; } void KisMainWindow::slotFileQuit() { if(!slotFileCloseAll()) return; close(); foreach(QPointer mainWin, KisPart::instance()->mainWindows()) { if (mainWin != this) { if(!mainWin->slotFileCloseAll()) return; mainWin->close(); } } } void KisMainWindow::slotFilePrint() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; applyDefaultSettings(printJob->printer()); QPrintDialog *printDialog = activeView()->createPrintDialog( printJob, this ); if (printDialog && printDialog->exec() == QDialog::Accepted) { printJob->printer().setPageMargins(0.0, 0.0, 0.0, 0.0, QPrinter::Point); printJob->printer().setPaperSize(QSizeF(activeView()->image()->width() / (72.0 * activeView()->image()->xRes()), activeView()->image()->height()/ (72.0 * activeView()->image()->yRes())), QPrinter::Inch); printJob->startPrinting(KisPrintJob::DeleteWhenDone); } else { delete printJob; } delete printDialog; } void KisMainWindow::slotFilePrintPreview() { if (!activeView()) return; KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return; /* Sets the startPrinting() slot to be blocking. The Qt print-preview dialog requires the printing to be completely blocking and only return when the full document has been printed. By default the KisPrintingDialog is non-blocking and multithreading, setting blocking to true will allow it to be used in the preview dialog */ printJob->setProperty("blocking", true); QPrintPreviewDialog *preview = new QPrintPreviewDialog(&printJob->printer(), this); printJob->setParent(preview); // will take care of deleting the job connect(preview, SIGNAL(paintRequested(QPrinter*)), printJob, SLOT(startPrinting())); preview->exec(); delete preview; } KisPrintJob* KisMainWindow::exportToPdf(const QString &pdfFileName) { if (!activeView()) return 0; KoPageLayout pageLayout; pageLayout = activeView()->pageLayout(); return exportToPdf(pageLayout, pdfFileName); } KisPrintJob* KisMainWindow::exportToPdf(KoPageLayout pageLayout, QString pdfFileName) { if (!activeView()) return 0; if (!activeView()->document()) return 0; if (pdfFileName.isEmpty()) { KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs"); QString defaultDir = group.readEntry("SavePdfDialog"); if (defaultDir.isEmpty()) defaultDir = QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation); QUrl startUrl = QUrl(defaultDir); KisDocument* pDoc = d->activeView->document(); /** if document has a file name, take file name and replace extension with .pdf */ if (pDoc && pDoc->url().isValid()) { startUrl = pDoc->url(); QString fileName = startUrl.fileName(); fileName = fileName.replace( QRegExp( "\\.\\w{2,5}$", Qt::CaseInsensitive ), ".pdf" ); startUrl = startUrl.adjusted(QUrl::RemoveFilename); startUrl.setPath(startUrl.path() + fileName ); } QPointer layoutDlg(new KoPageLayoutDialog(this, pageLayout)); layoutDlg->setWindowModality(Qt::WindowModal); if (layoutDlg->exec() != QDialog::Accepted || !layoutDlg) { delete layoutDlg; return 0; } pageLayout = layoutDlg->pageLayout(); delete layoutDlg; KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveDocument"); dialog.setCaption(i18n("Export as PDF")); dialog.setDefaultDir(startUrl.toLocalFile()); dialog.setMimeTypeFilters(QStringList() << "application/pdf"); QUrl url = QUrl::fromUserInput(dialog.filename()); pdfFileName = url.toLocalFile(); if (pdfFileName.isEmpty()) return 0; } KisPrintJob *printJob = activeView()->createPrintJob(); if (printJob == 0) return 0; if (isHidden()) { printJob->setProperty("noprogressdialog", true); } applyDefaultSettings(printJob->printer()); // TODO for remote files we have to first save locally and then upload. printJob->printer().setOutputFileName(pdfFileName); printJob->printer().setDocName(pdfFileName); printJob->printer().setColorMode(QPrinter::Color); if (pageLayout.format == KoPageFormat::CustomSize) { printJob->printer().setPaperSize(QSizeF(pageLayout.width, pageLayout.height), QPrinter::Millimeter); } else { printJob->printer().setPaperSize(KoPageFormat::printerPageSize(pageLayout.format)); } printJob->printer().setPageMargins(pageLayout.leftMargin, pageLayout.topMargin, pageLayout.rightMargin, pageLayout.bottomMargin, QPrinter::Millimeter); switch (pageLayout.orientation) { case KoPageFormat::Portrait: printJob->printer().setOrientation(QPrinter::Portrait); break; case KoPageFormat::Landscape: printJob->printer().setOrientation(QPrinter::Landscape); break; } //before printing check if the printer can handle printing if (!printJob->canPrint()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Cannot export to the specified file")); } printJob->startPrinting(KisPrintJob::DeleteWhenDone); return printJob; } void KisMainWindow::slotConfigureKeys() { KisPart::instance()->configureShortcuts(); emit keyBindingsChanged(); } void KisMainWindow::slotConfigureToolbars() { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); KEditToolBar edit(factory(), this); connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig())); (void) edit.exec(); applyToolBarLayout(); } void KisMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(KSharedConfig::openConfig()->group("krita")); KXMLGUIFactory *factory = guiFactory(); Q_UNUSED(factory); // Check if there's an active view if (!d->activeView) return; plugActionList("toolbarlist", d->toolbarList); applyToolBarLayout(); } void KisMainWindow::slotToolbarToggled(bool toggle) { //dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true; // The action (sender) and the toolbar have the same name KToolBar * bar = toolBar(sender()->objectName()); if (bar) { if (toggle) { bar->show(); } else { bar->hide(); } if (d->activeView && d->activeView->document()) { KConfigGroup group = KSharedConfig::openConfig()->group("krita"); saveMainWindowSettings(group); } } else warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!"; } void KisMainWindow::viewFullscreen(bool fullScreen) { KisConfig cfg; cfg.setFullscreenMode(fullScreen); if (fullScreen) { setWindowState(windowState() | Qt::WindowFullScreen); // set } else { setWindowState(windowState() & ~Qt::WindowFullScreen); // reset } } void KisMainWindow::slotProgress(int value) { qApp->processEvents(); if (!d->progressMutex.tryLock()) return; dbgUI << "KisMainWindow::slotProgress" << value; if (value <= -1 || value >= 100) { if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->firstTime = true; d->progressMutex.unlock(); return; } if (d->firstTime || !d->progress) { // The statusbar might not even be created yet. // So check for that first, and create it if necessary QStatusBar *bar = findChild(); if (!bar) { statusBar()->show(); QApplication::sendPostedEvents(this, QEvent::ChildAdded); } if (d->progress) { statusBar()->removeWidget(d->progress); delete d->progress; d->progress = 0; } d->progress = new QProgressBar(statusBar()); d->progress->setMaximumHeight(statusBar()->fontMetrics().height()); d->progress->setRange(0, 100); statusBar()->addPermanentWidget(d->progress); d->progress->show(); d->firstTime = false; } if (!d->progress.isNull()) { d->progress->setValue(value); } qApp->processEvents(); d->progressMutex.unlock(); } void KisMainWindow::setMaxRecentItems(uint _number) { d->recentFiles->setMaxItems(_number); } void KisMainWindow::slotReloadFile() { KisDocument* document = d->activeView->document(); if (!document || document->url().isEmpty()) return; if (document->isModified()) { bool ok = QMessageBox::question(this, i18nc("@title:window", "Krita"), i18n("You will lose all changes made since your last save\n" "Do you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes; if (!ok) return; } QUrl url = document->url(); saveWindowSettings(); if (!document->reload()) { QMessageBox::critical(this, i18nc("@title:window", "Krita"), i18n("Error: Could not reload this document")); } return; } void KisMainWindow::slotImportFile() { dbgUI << "slotImportFile()"; d->isImporting = true; slotFileOpen(); d->isImporting = false; } void KisMainWindow::slotExportFile() { dbgUI << "slotExportFile()"; d->isExporting = true; slotFileSaveAs(); d->isExporting = false; } bool KisMainWindow::isImporting() const { return d->isImporting; } bool KisMainWindow::isExporting() const { return d->isExporting; } QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory) { QDockWidget* dockWidget = 0; if (!d->dockWidgetsMap.contains(factory->id())) { dockWidget = factory->createDockWidget(); // It is quite possible that a dock factory cannot create the dock; don't // do anything in that case. if (!dockWidget) { warnKrita << "Could not create docker for" << factory->id(); return 0; } KoDockWidgetTitleBar *titleBar = dynamic_cast(dockWidget->titleBarWidget()); // Check if the dock widget is supposed to be collapsable if (!dockWidget->titleBarWidget()) { titleBar = new KoDockWidgetTitleBar(dockWidget); dockWidget->setTitleBarWidget(titleBar); titleBar->setCollapsable(factory->isCollapsable()); } titleBar->setFont(KoDockRegistry::dockFont()); dockWidget->setObjectName(factory->id()); dockWidget->setParent(this); if (dockWidget->widget() && dockWidget->widget()->layout()) dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1); Qt::DockWidgetArea side = Qt::RightDockWidgetArea; bool visible = true; switch (factory->defaultDockPosition()) { case KoDockFactoryBase::DockTornOff: dockWidget->setFloating(true); // position nicely? break; case KoDockFactoryBase::DockTop: side = Qt::TopDockWidgetArea; break; case KoDockFactoryBase::DockLeft: side = Qt::LeftDockWidgetArea; break; case KoDockFactoryBase::DockBottom: side = Qt::BottomDockWidgetArea; break; case KoDockFactoryBase::DockRight: side = Qt::RightDockWidgetArea; break; case KoDockFactoryBase::DockMinimized: default: side = Qt::RightDockWidgetArea; visible = false; } KConfigGroup group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); side = static_cast(group.readEntry("DockArea", static_cast(side))); if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea; addDockWidget(side, dockWidget); if (!visible) { dockWidget->hide(); } bool collapsed = factory->defaultCollapsed(); bool locked = false; group = KSharedConfig::openConfig()->group("krita").group("DockWidget " + factory->id()); collapsed = group.readEntry("Collapsed", collapsed); locked = group.readEntry("Locked", locked); //dbgKrita << "docker" << factory->id() << dockWidget << "collapsed" << collapsed << "locked" << locked << "titlebar" << titleBar; if (titleBar && collapsed) titleBar->setCollapsed(true); if (titleBar && locked) titleBar->setLocked(true); d->dockWidgetsMap.insert(factory->id(), dockWidget); } else { dockWidget = d->dockWidgetsMap[factory->id()]; } #ifdef Q_OS_MAC dockWidget->setAttribute(Qt::WA_MacSmallSize, true); #endif dockWidget->setFont(KoDockRegistry::dockFont()); connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts())); return dockWidget; } void KisMainWindow::forceDockTabFonts() { foreach(QObject *child, children()) { if (child->inherits("QTabBar")) { ((QTabBar *)child)->setFont(KoDockRegistry::dockFont()); } } } QList KisMainWindow::dockWidgets() const { return d->dockWidgetsMap.values(); } QList KisMainWindow::canvasObservers() const { QList observers; foreach(QDockWidget *docker, dockWidgets()) { KoCanvasObserverBase *observer = dynamic_cast(docker); if (observer) { observers << observer; } else { warnKrita << docker << "is not a canvas observer"; } } return observers; } void KisMainWindow::toggleDockersVisibility(bool visible) { if (!visible) { d->dockerStateBeforeHiding = saveState(); foreach(QObject* widget, children()) { if (widget->inherits("QDockWidget")) { QDockWidget* dw = static_cast(widget); if (dw->isVisible()) { dw->hide(); } } } } else { restoreState(d->dockerStateBeforeHiding); } } void KisMainWindow::setToolbarList(QList toolbarList) { qDeleteAll(d->toolbarList); d->toolbarList = toolbarList; } void KisMainWindow::slotDocumentTitleModified(const QString &caption, bool mod) { updateCaption(caption, mod); updateReloadFileAction(d->activeView ? d->activeView->document() : 0); } void KisMainWindow::subWindowActivated() { bool enabled = (activeKisView() != 0); d->mdiCascade->setEnabled(enabled); d->mdiNextWindow->setEnabled(enabled); d->mdiPreviousWindow->setEnabled(enabled); d->mdiTile->setEnabled(enabled); d->close->setEnabled(enabled); d->closeAll->setEnabled(enabled); setActiveSubWindow(d->mdiArea->activeSubWindow()); foreach(QToolBar *tb, toolBars()) { if (tb->objectName() == "BrushesAndStuff") { tb->setEnabled(enabled); } } updateCaption(); d->viewManager->actionManager()->updateGUI(); } void KisMainWindow::updateWindowMenu() { QMenu *menu = d->windowMenu->menu(); menu->clear(); menu->addAction(d->newWindow); menu->addAction(d->documentMenu); QMenu *docMenu = d->documentMenu->menu(); docMenu->clear(); foreach (QPointer doc, KisPart::instance()->documents()) { if (doc) { QString title = doc->url().toDisplayString(); if (title.isEmpty()) title = doc->image()->objectName(); QAction *action = docMenu->addAction(title); action->setIcon(qApp->windowIcon()); connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map())); d->documentMapper->setMapping(action, doc); } } menu->addSeparator(); menu->addAction(d->close); menu->addAction(d->closeAll); if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) { menu->addSeparator(); menu->addAction(d->mdiTile); menu->addAction(d->mdiCascade); } menu->addSeparator(); menu->addAction(d->mdiNextWindow); menu->addAction(d->mdiPreviousWindow); menu->addSeparator(); QList windows = d->mdiArea->subWindowList(); for (int i = 0; i < windows.size(); ++i) { QPointerchild = qobject_cast(windows.at(i)->widget()); if (child) { QString text; if (i < 9) { text = i18n("&%1 %2", i + 1, child->document()->url().toDisplayString()); } else { text = i18n("%1 %2", i + 1, child->document()->url().toDisplayString()); } QAction *action = menu->addAction(text); action->setIcon(qApp->windowIcon()); action->setCheckable(true); action->setChecked(child == activeKisView()); connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map())); d->windowMapper->setMapping(action, windows.at(i)); } } updateCaption(); } void KisMainWindow::setActiveSubWindow(QWidget *window) { if (!window) return; QMdiSubWindow *subwin = qobject_cast(window); //dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow; if (subwin && subwin != d->activeSubWindow) { KisView *view = qobject_cast(subwin->widget()); //dbgKrita << "\t" << view << activeView(); if (view && view != activeView()) { d->viewManager->setCurrentView(view); setActiveView(view); } d->activeSubWindow = subwin; } updateWindowMenu(); d->viewManager->actionManager()->updateGUI(); } void KisMainWindow::configChanged() { KisConfig cfg; QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView); d->mdiArea->setViewMode(viewMode); foreach(QMdiSubWindow *subwin, d->mdiArea->subWindowList()) { subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL())); } KConfigGroup group( KSharedConfig::openConfig(), "theme"); d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark")); d->viewManager->actionManager()->updateGUI(); QBrush brush(cfg.getMDIBackgroundColor()); d->mdiArea->setBackground(brush); QString backgroundImage = cfg.getMDIBackgroundImage(); if (backgroundImage != "") { QImage image(backgroundImage); QBrush brush(image); d->mdiArea->setBackground(brush); } d->mdiArea->update(); } void KisMainWindow::newView(QObject *document) { KisDocument *doc = qobject_cast(document); KisView *view = KisPart::instance()->createView(doc, resourceManager(), actionCollection(), this); addView(view); d->viewManager->actionManager()->updateGUI(); } void KisMainWindow::newWindow() { KisPart::instance()->createMainWindow()->show(); } void KisMainWindow::closeCurrentWindow() { d->mdiArea->currentSubWindow()->close(); d->viewManager->actionManager()->updateGUI(); } void KisMainWindow::checkSanity() { // print error if the lcms engine is not available if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) { // need to wait 1 event since exiting here would not work. m_errorMessage = i18n("The Calligra LittleCMS color management plugin is not installed. Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); if (rserver->resources().isEmpty()) { m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now."); m_dieOnError = true; QTimer::singleShot(0, this, SLOT(showErrorAndDie())); return; } } void KisMainWindow::showErrorAndDie() { QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage); if (m_dieOnError) { exit(10); } } void KisMainWindow::showAboutApplication() { KisAboutApplication dlg(this); dlg.exec(); } QPointerKisMainWindow::activeKisView() { if (!d->mdiArea) return 0; QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow(); //dbgKrita << "activeKisView" << activeSubWindow; if (!activeSubWindow) return 0; return qobject_cast(activeSubWindow->widget()); } void KisMainWindow::newOptionWidgets(const QList > &optionWidgetList) { foreach(QWidget *w, optionWidgetList) { #ifdef Q_OS_MAC w->setAttribute(Qt::WA_MacSmallSize, true); #endif w->setFont(KoDockRegistry::dockFont()); } if (d->toolOptionsDocker) { d->toolOptionsDocker->setOptionWidgets(optionWidgetList); } else { d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList); } } void KisMainWindow::applyDefaultSettings(QPrinter &printer) { if (!d->activeView) return; QString title = d->activeView->document()->documentInfo()->aboutInfo("title"); if (title.isEmpty()) { title = d->activeView->document()->url().fileName(); // strip off the native extension (I don't want foobar.kwd.ps when printing into a file) QMimeDatabase db; QMimeType mime = db.mimeTypeForName(d->activeView->document()->outputMimeType()); if (mime.isValid()) { QString extension = mime.preferredSuffix(); if (title.endsWith(extension)) title.chop(extension.length()); } } if (title.isEmpty()) { // #139905 title = i18n("%1 unsaved document (%2)", qApp->applicationDisplayName(), QLocale().toString(QDate::currentDate(), QLocale::ShortFormat)); } printer.setDocName(title); } void KisMainWindow::createActions() { KisActionManager *actionManager = d->viewManager->actionManager(); actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew())); actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen())); d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection()); connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles())); KSharedConfigPtr configPtr = KSharedConfig::openConfig(); d->recentFiles->loadEntries(configPtr->group("RecentFiles")); d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave())); d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs())); d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE); d->printAction = actionManager->createStandardAction(KStandardAction::Print, this, SLOT(slotFilePrint())); d->printAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->printActionPreview = actionManager->createStandardAction(KStandardAction::PrintPreview, this, SLOT(slotFilePrintPreview())); d->printActionPreview->setActivationFlags(KisAction::ACTIVE_IMAGE); d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo())); d->undo ->setActivationFlags(KisAction::ACTIVE_IMAGE); d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo())); d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE); d->exportPdf = new KisAction(i18nc("@action:inmenu", "Export as PDF...")); d->exportPdf->setActivationFlags(KisAction::ACTIVE_IMAGE); d->exportPdf->setIcon(KisIconUtils::loadIcon("application-pdf")); actionManager->addAction("file_export_pdf", d->exportPdf); connect(d->exportPdf, SIGNAL(triggered()), this, SLOT(exportToPdf())); actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit())); d->closeAll = new KisAction(i18nc("@action:inmenu", "Close All")); d->closeAll->setActivationFlags(KisAction::ACTIVE_IMAGE); d->closeAll->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W)); actionManager->addAction("file_close_all", d->closeAll); connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll())); // d->reloadFile = new KisAction(i18nc("@action:inmenu", "Reload")); // d->reloadFile->setActivationFlags(KisAction::CURRENT_IMAGE_MODIFIED); // actionManager->addAction("file_reload_file", d->reloadFile); // connect(d->reloadFile, SIGNAL(triggered(bool)), this, SLOT(slotReloadFile())); d->importFile = new KisAction(KisIconUtils::loadIcon("document-import"), i18nc("@action:inmenu", "Open ex&isting Document as Untitled Document...")); actionManager->addAction("file_import_file", d->importFile); connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile())); d->exportFile = new KisAction(KisIconUtils::loadIcon("document-export"), i18nc("@action:inmenu", "E&xport...")); d->exportFile->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager->addAction("file_export_file", d->exportFile); connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile())); /* The following entry opens the document information dialog. Since the action is named so it intends to show data this entry should not have a trailing ellipses (...). */ d->showDocumentInfo = new KisAction(KisIconUtils::loadIcon("configure"), i18nc("@action:inmenu", "Document Information")); d->showDocumentInfo->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager->addAction("file_documentinfo", d->showDocumentInfo); connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo())); actionManager->createStandardAction(KStandardAction::KeyBindings, this, SLOT(slotConfigureKeys())); actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars())); d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this)); d->themeManager->registerThemeActions(actionCollection()); connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged())); actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool))); d->toggleDockers = new KisAction(i18nc("@action:inmenu", "Show Dockers")); d->toggleDockers->setCheckable(true); d->toggleDockers->setChecked(true); actionManager->addAction("view_toggledockers", d->toggleDockers); connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool))); d->toggleDockerTitleBars = new KisAction(i18nc("@action:inmenu", "Show Docker Titlebars")); d->toggleDockerTitleBars->setCheckable(true); KisConfig cfg; d->toggleDockerTitleBars->setChecked(cfg.showDockerTitleBars()); actionManager->addAction("view_toggledockertitlebars", d->toggleDockerTitleBars); connect(d->toggleDockerTitleBars, SIGNAL(toggled(bool)), SLOT(showDockerTitleBars(bool))); actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu); actionCollection()->addAction("window", d->windowMenu); d->mdiCascade = new KisAction(i18nc("@action:inmenu", "Cascade")); d->mdiCascade->setActivationFlags(KisAction::MULTIPLE_IMAGES); actionManager->addAction("windows_cascade", d->mdiCascade); connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows())); d->mdiTile = new KisAction(i18nc("@action:inmenu", "Tile")); d->mdiTile->setActivationFlags(KisAction::MULTIPLE_IMAGES); actionManager->addAction("windows_tile", d->mdiTile); connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows())); d->mdiNextWindow = new KisAction(i18nc("@action:inmenu", "Next")); d->mdiNextWindow->setActivationFlags(KisAction::MULTIPLE_IMAGES); actionManager->addAction("windows_next", d->mdiNextWindow); connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow())); d->mdiPreviousWindow = new KisAction(i18nc("@action:inmenu", "Previous")); d->mdiPreviousWindow->setActivationFlags(KisAction::MULTIPLE_IMAGES); actionCollection()->addAction("windows_previous", d->mdiPreviousWindow); connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow())); d->newWindow = new KisAction(KisIconUtils::loadIcon("window-new"), i18nc("@action:inmenu", "&New Window")); actionManager->addAction("view_newwindow", d->newWindow); connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow())); d->close = new KisAction(i18nc("@action:inmenu", "Close")); d->close->setActivationFlags(KisAction::ACTIVE_IMAGE); connect(d->close, SIGNAL(triggered()), SLOT(closeCurrentWindow())); actionManager->addAction("file_close", d->close); actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences())); for (int i = 0; i < 2; i++) { d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer")); d->expandingSpacers[i]->setDefaultWidget(new QWidget(this)); d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]); } } void KisMainWindow::applyToolBarLayout() { const bool isPlastiqueStyle = style()->objectName() == "plastique"; Q_FOREACH (KToolBar *toolBar, toolBars()) { toolBar->layout()->setSpacing(4); if (isPlastiqueStyle) { toolBar->setContentsMargins(0, 0, 0, 2); } } } void KisMainWindow::initializeGeometry() { // if the user didn's specify the geometry on the command line (does anyone do that still?), // we first figure out some good default size and restore the x,y position. See bug 285804Z. KConfigGroup cfg( KSharedConfig::openConfig(), "MainWindow"); if (!initialGeometrySet()) { QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray())); if (!restoreGeometry(geom)) { const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->availableGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) { desk = QApplication::desktop()->availableGeometry(QApplication::desktop()->screen(scnum)); } quint32 x = desk.x(); quint32 y = desk.y(); quint32 w = 0; quint32 h = 0; // Default size -- maximize on small screens, something useful on big screens const int deskWidth = desk.width(); if (deskWidth > 1024) { // a nice width, and slightly less than total available // height to componensate for the window decs w = (deskWidth / 3) * 2; h = (desk.height() / 3) * 2; } else { w = desk.width(); h = desk.height(); } x += (desk.width() - w) / 2; y += (desk.height() - h) / 2; move(x,y); setGeometry(geometry().x(), geometry().y(), w, h); } } restoreWorkspace(QByteArray::fromBase64(cfg.readEntry("ko_windowstate", QByteArray()))); } void KisMainWindow::showManual() { QDesktopServices::openUrl(QUrl("https://userbase.kde.org/Special:MyLanguage/Krita/Manual")); } void KisMainWindow::showDockerTitleBars(bool show) { foreach (QDockWidget *dock, dockWidgets()) { if (dock->titleBarWidget()) { const bool isCollapsed = (dock->widget() && dock->widget()->isHidden()) || !dock->widget(); dock->titleBarWidget()->setVisible(show || (dock->isFloating() && isCollapsed)); } } KisConfig cfg; cfg.setShowDockerTitleBars(show); } void KisMainWindow::moveEvent(QMoveEvent *e) { if (qApp->desktop()->screenNumber(this) != qApp->desktop()->screenNumber(e->oldPos())) { KisConfigNotifier::instance()->notifyConfigChanged(); } } #include #include #include diff --git a/krita/ui/KisOpenPane.h b/krita/ui/KisOpenPane.h index 7f454dde1e..ed4c0e8a62 100644 --- a/krita/ui/KisOpenPane.h +++ b/krita/ui/KisOpenPane.h @@ -1,105 +1,104 @@ /* This file is part of the KDE project Copyright (C) 2005 Peter Simonsson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISOPENPANE_H #define KISOPENPANE_H #include #include #include #include -class KConfig; class KisOpenPanePrivate; class QPixmap; class KisTemplatesPane; class KisDetailsPane; class QUrl; class QTreeWidgetItem; class QString; class QStringList; /// \internal class KisOpenPane : public QDialog { Q_OBJECT public: /** * Constructor * @param parent the parent widget * @param templateType the template-type (group) that should be selected on creation. */ KisOpenPane(QWidget *parent, const QStringList& mimeFilter, const QString& templatesResourcePath = QString()); virtual ~KisOpenPane(); QTreeWidgetItem* addPane(const QString &title, const QString &iconName, QWidget *widget, int sortWeight); QTreeWidgetItem* addPane(const QString& title, const QPixmap& icon, QWidget* widget, int sortWeight); /** * If the application has a way to create a document not based on a template, but on user * provided settings, the widget showing these gets set here. * @see KisDocument::createCustomDocumentWidget() * @param widget the widget. * @param title the title shown in the sidebar * @param icon the icon shown in the sidebar */ void addCustomDocumentWidget(QWidget *widget, const QString& title = QString(), const QString& icon = QString()); protected Q_SLOTS: void updateSelectedWidget(); void itemClicked(QTreeWidgetItem* item); /// Saves the splitter sizes for KisDetailsPaneBase based panes void saveSplitterSizes(KisDetailsPane* sender, const QList& sizes); private Q_SLOTS: /// when clicked "Open Existing Document" button void openFileDialog(); Q_SIGNALS: void openExistingFile(const QUrl&); void openTemplate(const QUrl&); /// Emitted when the always use template has changed void alwaysUseChanged(KisTemplatesPane* sender, const QString& alwaysUse); /// Emitted when one of the detail panes have changed it's splitter void splitterResized(KisDetailsPane* sender, const QList& sizes); void cancelButton(); protected: void initRecentDocs(); /** * Populate the list with all templates the user can choose. * @param templatesResourcePath the template-type (group) that should be selected on creation. */ void initTemplates(const QString& templatesResourcePath); // QWidget overrides virtual void dragEnterEvent(QDragEnterEvent * event); virtual void dropEvent(QDropEvent * event); private: QStringList m_mimeFilter; KisOpenPanePrivate * const d; }; #endif //KOOPENPANE_H diff --git a/krita/ui/KisPart.cpp b/krita/ui/KisPart.cpp index 218ff96aa8..752178fe95 100644 --- a/krita/ui/KisPart.cpp +++ b/krita/ui/KisPart.cpp @@ -1,648 +1,646 @@ /* This file is part of the KDE project * Copyright (C) 1998, 1999 Torben Weis * Copyright (C) 2000-2005 David Faure * Copyright (C) 2007-2008 Thorsten Zachmann * Copyright (C) 2010-2012 Boudewijn Rempt * Copyright (C) 2011 Inge Wallin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KisPart.h" #include "KoProgressProxy.h" #include #include #include #include #include #include #include #include #include "KisApplication.h" -#include "KisMainWindow.h" #include "KisDocument.h" #include "KisView.h" #include "KisViewManager.h" #include "KisOpenPane.h" #include "KisImportExportManager.h" #include "dialogs/KisShortcutsDialog.h" #include #include #include #include #include #include - #include #include #include #include #include #include #include #include #include #include #include #include #include "KisView.h" #include "KisDocument.h" #include "kis_config.h" #include "kis_clipboard.h" #include "kis_custom_image_widget.h" #include "kis_image_from_clipboard_widget.h" #include "kis_shape_controller.h" #include "kis_resource_server_provider.h" #include "KisImportExportManager.h" #include "KisDocumentEntry.h" #include "kis_color_manager.h" #include "kis_action.h" Q_GLOBAL_STATIC(KisPart, s_instance) class Q_DECL_HIDDEN KisPart::Private { public: Private(KisPart *_part) : part(_part) , canvasItem(0) , startupWidget(0) , actionCollection(0) { } ~Private() { delete canvasItem; } KisPart *part; QList > views; QList > mainWindows; QList > documents; QGraphicsItem *canvasItem; QString templatesResourcePath; KisOpenPane *startupWidget; KActionCollection *actionCollection; void loadActions(); }; void KisPart::Private::loadActions() { actionCollection = new KActionCollection(part, "krita"); KoResourcePaths::addResourceType("kis_actions", "data", "krita/actions/"); QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates); foreach(const QString &actionDefinition, actionDefinitions) { QDomDocument doc; QFile f(actionDefinition); f.open(QFile::ReadOnly); doc.setContent(f.readAll()); QDomElement e = doc.documentElement(); // Actions QString collection = e.attribute("name"); e = e.firstChild().toElement(); // Action while (!e.isNull()) { if (e.tagName() == "Action") { QString name = e.attribute("name"); QString icon = e.attribute("icon"); QString text = i18n(e.attribute("text").toUtf8().constData()); QString whatsthis = i18n(e.attribute("whatsThis").toUtf8().constData()); QString toolTip = i18n(e.attribute("toolTip").toUtf8().constData()); QString statusTip = i18n(e.attribute("statusTip").toUtf8().constData()); QString iconText = i18n(e.attribute("iconText").toUtf8().constData()); QKeySequence shortcut = QKeySequence(e.attribute("shortcut")); bool isCheckable = e.attribute("isCheckable") == "true" ? true : false; QKeySequence defaultShortcut = QKeySequence(e.attribute("defaultShortcut")); if (name.isEmpty()) { dbgKrita << text << "has no name! From:" << actionDefinition; } KisAction *action = new KisAction(KisIconUtils::loadIcon(icon.toLatin1()), text); action->setObjectName(name); action->setWhatsThis(whatsthis); action->setToolTip(toolTip); action->setStatusTip(statusTip); action->setIconText(iconText); action->setDefaultShortcut(shortcut); action->setCheckable(isCheckable); if (!actionCollection->action(name)) { actionCollection->addAction(name, action); } // else { // dbgKrita << "duplicate action" << name << action << "from" << collection; // delete action; // } } e = e.nextSiblingElement(); } actionCollection->readSettings(); } //check for colliding shortcuts QMap existingShortcuts; foreach(QAction* action, actionCollection->actions()) { if(action->shortcut() == QKeySequence(0)) { continue; } if (existingShortcuts.contains(action->shortcut())) { dbgKrita << "action" << action->text() << "and" << existingShortcuts[action->shortcut()]->text() << "have the same shortcut:" << action->shortcut(); } else { existingShortcuts[action->shortcut()] = action; } } } KisPart* KisPart::instance() { return s_instance; } KisPart::KisPart() : d(new Private(this)) { setTemplatesResourcePath(QLatin1String("krita/templates/")); // Preload all the resources in the background Q_UNUSED(KoResourceServerProvider::instance()); Q_UNUSED(KisResourceServerProvider::instance()); Q_UNUSED(KisColorManager::instance()); } KisPart::~KisPart() { while (!d->documents.isEmpty()) { delete d->documents.takeFirst(); } while (!d->views.isEmpty()) { delete d->views.takeFirst(); } while (!d->mainWindows.isEmpty()) { delete d->mainWindows.takeFirst(); } delete d; } void KisPart::addDocument(KisDocument *document) { //dbgKrita << "Adding document to part list" << document; Q_ASSERT(document); if (!d->documents.contains(document)) { d->documents.append(document); emit documentOpened('/'+objectName()); } } QList > KisPart::documents() const { return d->documents; } KisDocument *KisPart::createDocument() const { KisDocument *doc = new KisDocument(); return doc; } int KisPart::documentCount() const { return d->documents.size(); } void KisPart::removeDocument(KisDocument *document) { d->documents.removeAll(document); emit documentClosed('/'+objectName()); document->deleteLater(); } KisMainWindow *KisPart::createMainWindow() { KisMainWindow *mw = new KisMainWindow(); addMainWindow(mw); return mw; } KisView *KisPart::createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) { QApplication::setOverrideCursor(Qt::WaitCursor); KisView *view = new KisView(document, resourceManager, actionCollection, parent); QApplication::restoreOverrideCursor(); addView(view); return view; } void KisPart::addView(KisView *view) { if (!view) return; if (!d->views.contains(view)) { d->views.append(view); } connect(view, SIGNAL(destroyed()), this, SLOT(viewDestroyed())); emit sigViewAdded(view); } void KisPart::removeView(KisView *view) { if (!view) return; emit sigViewRemoved(view); QPointer doc = view->document(); d->views.removeAll(view); if (doc) { bool found = false; foreach(QPointer view, d->views) { if (view && view->document() == doc) { found = true; break; } } if (!found) { removeDocument(doc); } } } QList > KisPart::views() const { return d->views; } int KisPart::viewCount(KisDocument *doc) const { if (!doc) { return d->views.count(); } else { int count = 0; foreach(QPointer view, d->views) { if (view->document() == doc) { count++; } } return count; } } QGraphicsItem *KisPart::canvasItem(KisDocument *document, bool create) { if (create && !d->canvasItem) { d->canvasItem = createCanvasItem(document); } return d->canvasItem; } QGraphicsItem *KisPart::createCanvasItem(KisDocument *document) { if (!document) return 0; KisView *view = createView(document, 0, 0, 0); QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); QWidget *canvasController = view->findChild(); proxy->setWidget(canvasController); return proxy; } void KisPart::addMainWindow(KisMainWindow *mainWindow) { if (!mainWindow) return; if (d->mainWindows.contains(mainWindow)) return; dbgUI <<"mainWindow" << (void*)mainWindow <<"added to doc" << this; d->mainWindows.append(mainWindow); } void KisPart::removeMainWindow(KisMainWindow *mainWindow) { dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; if (mainWindow) { d->mainWindows.removeAll(mainWindow); } } const QList > &KisPart::mainWindows() const { return d->mainWindows; } int KisPart::mainwindowCount() const { return d->mainWindows.count(); } KisMainWindow *KisPart::currentMainwindow() const { QWidget *widget = qApp->activeWindow(); KisMainWindow *mainWindow = qobject_cast(widget); while (!mainWindow && widget) { widget = widget->parentWidget(); mainWindow = qobject_cast(widget); } if (!mainWindow && mainWindows().size() > 0) { mainWindow = mainWindows().first(); } return mainWindow; } void KisPart::openExistingFile(const QUrl &url) { qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); if (!document->openUrl(url)) { return; } document->setModified(false); addDocument(document); KisMainWindow *mw = 0; if (d->startupWidget) { mw = qobject_cast(d->startupWidget->parent()); } if (!mw) { mw = currentMainwindow(); } KisView *view = createView(document, mw->resourceManager(), mw->actionCollection(), mw); mw->addView(view); if (d->startupWidget) { d->startupWidget->setParent(0); d->startupWidget->hide(); } qApp->restoreOverrideCursor(); } void KisPart::configureShortcuts() { if (!d->actionCollection) { d->loadActions(); } // In kdelibs4 a hack was used to hide the shortcut schemes widget in the // normal shortcut editor KShortcutsDialog from kdelibs, by setting the // bottom buttons oneself. This does not work anymore (as the buttons are // no longer exposed), so for now running with a plain copy of the sources // of KShortcutsDialog, where the schemes editor is disabled directly. // Not nice, but then soon custom Krita-specific shortcut handling is // planned anyway. KisShortcutsDialog dlg(KShortcutsEditor::WidgetAction | KShortcutsEditor::WindowAction | KShortcutsEditor::ApplicationAction); dlg.addCollection(d->actionCollection); dlg.configure(); foreach(KisMainWindow *mainWindow, d->mainWindows) { KActionCollection *ac = mainWindow->actionCollection(); ac->readSettings(); // append shortcuts to tooltips if they exist foreach( QAction* tempAction, ac->actions()) { // find the shortcut pattern and delete (note the preceding space in the RegEx) QString strippedTooltip = tempAction->toolTip().remove(QRegExp("\\s\\(.*\\)")); // append shortcut if it exists for action if(tempAction->shortcut() == QKeySequence(0)) tempAction->setToolTip( strippedTooltip); else tempAction->setToolTip( strippedTooltip + " (" + tempAction->shortcut().toString() + ")"); } } } void KisPart::openTemplate(const QUrl &url) { qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); bool ok = document->loadNativeFormat(url.toLocalFile()); document->setModified(false); document->undoStack()->clear(); if (ok) { QMimeDatabase db; QString mimeType = db.mimeTypeForFile(url.path(), QMimeDatabase::MatchExtension).name(); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); document->setMimeTypeAfterLoading(mimeType); document->resetURL(); document->setEmpty(); } else { document->showLoadingErrorDialog(); document->initEmpty(); } addDocument(document); KisMainWindow *mw = qobject_cast(d->startupWidget->parent()); if (!mw) mw = currentMainwindow(); KisView *view = createView(document, mw->resourceManager(), mw->actionCollection(), mw); mw->addView(view); d->startupWidget->setParent(0); d->startupWidget->hide(); qApp->restoreOverrideCursor(); } void KisPart::viewDestroyed() { KisView *view = qobject_cast(sender()); if (view) { removeView(view); } } void KisPart::addRecentURLToAllMainWindows(QUrl url) { // Add to recent actions list in our mainWindows foreach(KisMainWindow *mainWindow, d->mainWindows) { mainWindow->addRecentURL(url); } } void KisPart::showStartUpWidget(KisMainWindow *mainWindow, bool alwaysShow) { #ifndef NDEBUG if (d->templatesResourcePath.isEmpty()) dbgUI << "showStartUpWidget called, but setTemplatesResourcePath() never called. This will not show a lot"; #endif if (!alwaysShow) { KConfigGroup cfgGrp( KSharedConfig::openConfig(), "TemplateChooserDialog"); QString fullTemplateName = cfgGrp.readPathEntry("AlwaysUseTemplate", QString()); if (!fullTemplateName.isEmpty()) { QUrl url(fullTemplateName); QFileInfo fi(url.toLocalFile()); if (!fi.exists()) { const QString templatesResourcePath = this->templatesResourcePath(); QString desktopfile = KoResourcePaths::findResource("data", templatesResourcePath + "*/" + fullTemplateName); if (desktopfile.isEmpty()) { desktopfile = KoResourcePaths::findResource("data", templatesResourcePath + fullTemplateName); } if (desktopfile.isEmpty()) { fullTemplateName.clear(); } else { KDesktopFile f(desktopfile); fullTemplateName = QFileInfo(desktopfile).absolutePath() + '/' + f.readUrl(); } } if (!fullTemplateName.isEmpty()) { openTemplate(QUrl::fromLocalFile(fullTemplateName)); return; } } } if (d->startupWidget) { delete d->startupWidget; } const QStringList mimeFilter = KisImportExportManager::mimeFilter(KIS_MIME_TYPE, KisImportExportManager::Import, KisDocumentEntry::extraNativeMimeTypes()); d->startupWidget = new KisOpenPane(0, mimeFilter, d->templatesResourcePath); d->startupWidget->setWindowModality(Qt::WindowModal); QList widgetList = createCustomDocumentWidgets(d->startupWidget); foreach(const CustomDocumentWidgetItem & item, widgetList) { d->startupWidget->addCustomDocumentWidget(item.widget, item.title, item.icon); connect(item.widget, SIGNAL(documentSelected(KisDocument*)), this, SLOT(startCustomDocument(KisDocument*))); } connect(d->startupWidget, SIGNAL(openExistingFile(const QUrl&)), this, SLOT(openExistingFile(const QUrl&))); connect(d->startupWidget, SIGNAL(openTemplate(const QUrl&)), this, SLOT(openTemplate(const QUrl&))); d->startupWidget->setParent(mainWindow); d->startupWidget->setWindowFlags(Qt::Dialog); d->startupWidget->exec(); } QList KisPart::createCustomDocumentWidgets(QWidget * parent) { KisConfig cfg; int w = cfg.defImageWidth(); int h = cfg.defImageHeight(); QList widgetList; { KisPart::CustomDocumentWidgetItem item; item.widget = new KisCustomImageWidget(parent, w, h, cfg.defImageResolution(), cfg.defColorModel(), cfg.defaultColorDepth(), cfg.defColorProfile(), i18n("Unnamed")); item.icon = "application-x-krita"; widgetList << item; } { QSize sz = KisClipboard::instance()->clipSize(); if (sz.isValid() && sz.width() != 0 && sz.height() != 0) { w = sz.width(); h = sz.height(); } KisPart::CustomDocumentWidgetItem item; item.widget = new KisImageFromClipboard(parent, w, h, cfg.defImageResolution(), cfg.defColorModel(), cfg.defaultColorDepth(), cfg.defColorProfile(), i18n("Unnamed")); item.title = i18n("Create from Clipboard"); item.icon = "klipper"; widgetList << item; } return widgetList; } void KisPart::setTemplatesResourcePath(const QString &templatesResourcePath) { Q_ASSERT(!templatesResourcePath.isEmpty()); Q_ASSERT(templatesResourcePath.endsWith(QLatin1Char('/'))); d->templatesResourcePath = templatesResourcePath; } QString KisPart::templatesResourcePath() const { return d->templatesResourcePath; } void KisPart::startCustomDocument(KisDocument* doc) { addDocument(doc); KisMainWindow *mw = qobject_cast(d->startupWidget->parent()); if (!mw) mw = currentMainwindow(); KisView *view = createView(doc, mw->resourceManager(), mw->actionCollection(), mw); mw->addView(view); d->startupWidget->setParent(0); d->startupWidget->hide(); } KisInputManager* KisPart::currentInputManager() { return instance()->currentMainwindow()->viewManager()->inputManager(); } #include #include #include diff --git a/krita/ui/KisPrintJob.h b/krita/ui/KisPrintJob.h index f8afddb38d..585910c71d 100644 --- a/krita/ui/KisPrintJob.h +++ b/krita/ui/KisPrintJob.h @@ -1,97 +1,95 @@ /* This file is part of the KDE project * Copyright (C) 2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KISPRINTJOB_H #define KISPRINTJOB_H #include #include #include #include #include "kritaui_export.h" #include -class QWidget; - /** * A print job is an interface that the KisView uses to create an application-specific * class that can take care of printing. * The printjob should be able to print again after a print job has been completed, * using the same QPrinter to allow the user to alter settings on the QPrinter and * call print again. * The printjob can thus see startPrinting() called more than once, and the implementation * of that signal should honor the removePolicy passed to it. */ class KRITAUI_EXPORT KisPrintJob : public QObject { Q_OBJECT public: /** * Constructor. * @param parent the parent qobject that is passed for memory management purposes. */ explicit KisPrintJob(KisImageWSP image); virtual ~KisPrintJob(); /// A policy to allow the printjob to delete itself after its done printing. enum RemovePolicy { DeleteWhenDone, ///< Delete the job when its done with printing. DoNotDelete ///< Keep the job around so it can be started again. }; /// Returns the printer that is used for this print job so others can alter the details of the print-job. QPrinter &printer() { return m_printer; } int documentFirstPage() const { return 1; } int documentLastPage() const { return 1; } int documentCurrentPage() const { return 1; } QAbstractPrintDialog::PrintDialogOptions printDialogOptions() const; /** *@brief Check if the painter can print to the printer *@returns true if the print job can print to the given printer */ bool canPrint(); public Q_SLOTS: /** * This is called every time the job should be executed. * When called the document should be printed a new painter using the printer * of this printJob in order to honor the settings the user made on the printer. * canPrint() should be called before startPrinting to check if the painter can print * to the printer * @param removePolicy a policy that should be honored so the caller can make sure * this job doesn't leak memory after being used. */ void startPrinting(RemovePolicy removePolicy = DoNotDelete); private: KisImageWSP m_image; QPrinter m_printer; }; #endif diff --git a/krita/ui/KisViewManager.cpp b/krita/ui/KisViewManager.cpp index a8a832e981..98e3310f7b 100644 --- a/krita/ui/KisViewManager.cpp +++ b/krita/ui/KisViewManager.cpp @@ -1,1344 +1,1343 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 José Luis Vergara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "KisViewManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "canvas/kis_perspective_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include "kis_image_manager.h" #include -#include "KisMainWindow.h" #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include "kis_progress_widget.h" #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "kra/kis_kra_loader.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" #include "kis_icon_utils.h" class StatusBarItem { public: StatusBarItem() // for QValueList : m_widget(0), m_connected(false), m_hidden(false) {} StatusBarItem(QWidget * widget, int stretch, bool permanent) : m_widget(widget), m_stretch(stretch), m_permanent(permanent), m_connected(false), m_hidden(false) {} bool operator==(const StatusBarItem& rhs) { return m_widget == rhs.m_widget; } bool operator!=(const StatusBarItem& rhs) { return m_widget != rhs.m_widget; } QWidget * widget() const { return m_widget; } void ensureItemShown(QStatusBar * sb) { Q_ASSERT(m_widget); if (!m_connected) { if (m_permanent) sb->addPermanentWidget(m_widget, m_stretch); else sb->addWidget(m_widget, m_stretch); if(!m_hidden) m_widget->show(); m_connected = true; } } void ensureItemHidden(QStatusBar * sb) { if (m_connected) { m_hidden = m_widget->isHidden(); sb->removeWidget(m_widget); m_widget->hide(); m_connected = false; } } private: QWidget * m_widget; int m_stretch; bool m_permanent; bool m_connected; bool m_hidden; }; class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate() : filterManager(0) , statusBar(0) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , wrapAroundAction(0) , showRulersAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , showGuidesAction(0) , selectionManager(0) , controlFrame(0) , nodeManager(0) , imageManager(0) , gridManager(0) , perspectiveGridManager(0) , paintingAssistantsManager(0) , actionManager(0) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(0) , canvasResourceManager(0) , guiUpdateCompressor(0) , actionCollection(0) , mirrorManager(0) , actionAuthor(0) { } ~KisViewManagerPrivate() { delete filterManager; delete selectionManager; delete nodeManager; delete imageManager; delete gridManager; delete perspectiveGridManager; delete paintingAssistantsManager; delete statusBar; delete actionManager; delete canvasControlsManager; delete canvasResourceProvider; delete canvasResourceManager; delete mirrorManager; } public: KisFilterManager *filterManager; KisStatusBar *statusBar; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *wrapAroundAction; KisAction *showRulersAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisAction *showGuidesAction; KisSelectionManager *selectionManager; KisControlFrame *controlFrame; KisNodeManager *nodeManager; KisImageManager *imageManager; KisGridManager *gridManager; KisCanvasControlsManager *canvasControlsManager; KisPerspectiveGridManager * perspectiveGridManager; KisPaintingAssistantsManager *paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager* actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider* canvasResourceProvider; KoCanvasResourceManager* canvasResourceManager; QList statusBarItems; KisSignalCompressor* guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager *mirrorManager; QPointer inputManager; KisSignalAutoConnectionsStore viewConnections; KSelectAction *actionAuthor; // Select action for author profile. }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate()) { d->actionCollection = _actionCollection; d->actionManager = new KisActionManager(this); d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider = new KisCanvasResourceProvider(this); d->canvasResourceManager = new KoCanvasResourceManager(); d->canvasResourceProvider->setResourceManager(d->canvasResourceManager); d->guiUpdateCompressor = new KisSignalCompressor(30, KisSignalCompressor::POSTPONE, this); connect(d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); createManagers(); d->controlFrame = new KisControlFrame(this, parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); KoCanvasController *dummy = new KoDummyCanvasController(actionCollection()); KoToolManager::instance()->registerTools(actionCollection(), dummy); d->statusBar = new KisStatusBar(this); QTimer::singleShot(0, this, SLOT(makeStatusBarVisible())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame->paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame->paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame->paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { d->inputManager->addTrackedCanvas(view->canvasBase()); } void KisViewManager::slotViewRemoved(KisView *view) { d->inputManager->removeTrackedCanvas(view->canvasBase()); } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(d->statusBar); d->viewConnections.clear(); } QPointerimageView = qobject_cast(view); if (imageView) { // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); d->currentImageView = imageView; KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEnd())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->currentImageView->canvasController(), SIGNAL(toolOptionWidgetsChanged(QList >)), mainWindow(), SLOT(newOptionWidgets(QList >))); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame->paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(toggleShowRulers(bool))); d->showRulersAction->setChecked(imageView->zoomManager()->horizontalRulerVisible() && imageView->zoomManager()->verticalRulerVisible()); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); d->viewConnections.addUniqueConnection(d->showGuidesAction, SIGNAL(triggered(bool)), imageView->zoomManager(), SLOT(showGuides(bool))); showHideScrollbars(); } d->filterManager->setView(imageView); d->selectionManager->setView(imageView); d->nodeManager->setView(imageView); d->imageManager->setView(imageView); d->canvasControlsManager->setView(imageView); d->actionManager->setView(imageView); d->gridManager->setView(imageView); d->statusBar->setView(imageView); d->paintingAssistantsManager->setView(imageView); d->perspectiveGridManager->setView(imageView); d->mirrorManager->setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); } d->actionManager->updateGUI(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( view->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); // Restore the last used brush preset if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString lastPreset = cfg.readEntry("LastPreset", QString("Basic_tip_default")); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName("Basic_tip_default"); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } } } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return d->statusBar; } void KisViewManager::addStatusBarItem(QWidget * widget, int stretch, bool permanent) { if (!mainWindow()) return; StatusBarItem item(widget, stretch, permanent); QStatusBar * sb = mainWindow()->statusBar(); if (sb) { item.ensureItemShown(sb); } d->statusBarItems.append(item); } void KisViewManager::removeStatusBarItem(QWidget * widget) { QStatusBar *sb = mainWindow()->statusBar(); int itemCount = d->statusBarItems.count(); for (int i = itemCount-1; i >= 0; --i) { StatusBarItem &sbItem = d->statusBarItems[i]; if (sbItem.widget() == widget) { if (sb) { sbItem.ensureItemHidden(sb); } d->statusBarItems.removeOne(sbItem); break; } } } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame->paintopBox(); } KoProgressUpdater* KisViewManager::createProgressUpdater(KoProgressUpdater::Mode mode) { return new KisProgressUpdater(d->statusBar->progress(), document()->progressProxy(), mode); } KisSelectionManager * KisViewManager::selectionManager() { return d->selectionManager; } KisNodeSP KisViewManager::activeNode() { if (d->nodeManager) return d->nodeManager->activeNode(); else return 0; } KisLayerSP KisViewManager::activeLayer() { if (d->nodeManager) return d->nodeManager->activeLayer(); else return 0; } KisPaintDeviceSP KisViewManager::activeDevice() { if (d->nodeManager) return d->nodeManager->activePaintDevice(); else return 0; } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return d->filterManager; } KisImageManager * KisViewManager::imageManager() { return d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { d->saveIncremental = new KisAction(i18n("Save Incremental &Version"), this); d->saveIncremental->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_S)); d->saveIncremental->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("save_incremental_version", d->saveIncremental); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = new KisAction(i18n("Save Incremental Backup"), this); d->saveIncrementalBackup->setDefaultShortcut(Qt::Key_F4); d->saveIncrementalBackup->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("save_incremental_backup", d->saveIncrementalBackup); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = new KisAction(i18n("Toggle Tablet Debugger"), this); actionManager()->addAction("tablet_debugger", tabletDebugger ); tabletDebugger->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T)); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = new KisAction( i18n( "&Create Template From Image..." ), this); d->createTemplate->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("create_template", d->createTemplate); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = new KisAction( i18n( "&Create Copy From Current Image" ), this); d->createCopy->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("create_copy", d->createCopy); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = new KisAction(i18n("Open Resources Folder"), this); d->openResourcesDirectory->setToolTip(i18n("Opens a file browser at the location Krita saves resources such as brushes to.")); d->openResourcesDirectory->setWhatsThis(i18n("Opens a file browser at the location Krita saves resources such as brushes to.")); actionManager()->addAction("open_resources_directory", d->openResourcesDirectory); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = new KisAction(i18n("Rotate Canvas Right"), this); actionManager()->addAction("rotate_canvas_right", d->rotateCanvasRight); d->rotateCanvasRight->setActivationFlags(KisAction::ACTIVE_IMAGE); d->rotateCanvasRight->setDefaultShortcut(QKeySequence("Ctrl+]")); d->rotateCanvasLeft = new KisAction(i18n("Rotate Canvas Left"), this); actionManager()->addAction("rotate_canvas_left", d->rotateCanvasLeft); d->rotateCanvasLeft->setActivationFlags(KisAction::ACTIVE_IMAGE); d->rotateCanvasLeft->setDefaultShortcut(QKeySequence("Ctrl+[")); d->wrapAroundAction = new KisAction(i18n("Wrap Around Mode"), this); d->wrapAroundAction->setCheckable(true); d->wrapAroundAction->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("wrap_around_mode", d->wrapAroundAction); d->wrapAroundAction->setDefaultShortcut(QKeySequence(Qt::Key_W)); KisAction *tAction = new KisAction(i18n("Show Status Bar"), this); tAction->setCheckable(true); tAction->setChecked(true); tAction->setToolTip(i18n("Shows or hides the status bar")); actionManager()->addAction("showStatusBar", tAction); tAction->setActivationFlags(KisAction::ACTIVE_IMAGE); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = new KisAction(i18n("Show Canvas Only"), this); tAction->setActivationFlags(KisAction::NONE); tAction->setCheckable(true); tAction->setToolTip(i18n("Shows just the canvas or the whole window")); QList shortcuts; shortcuts << QKeySequence(Qt::Key_Tab); tAction->setShortcuts(shortcuts); tAction->setChecked(false); actionManager()->addAction("view_show_just_the_canvas", tAction); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showJustTheCanvas(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); a->setActivationConditions(KisAction::SELECTION_EDITABLE); } a = new KisAction(i18n("Cleanup removed files..."), this); actionManager()->addAction("edit_blacklist_cleanup", a); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); d->showRulersAction = new KisAction(i18n("Show Rulers"), this); d->showRulersAction->setCheckable(true); d->showRulersAction->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("view_ruler", d->showRulersAction); d->showRulersAction->setWhatsThis(i18n("The rulers show the horizontal and vertical positions of the mouse on the image " "and can be used to position your mouse at the right place on the canvas.

Uncheck this to hide the rulers.

")); KisConfig cfg; d->showRulersAction->setChecked(cfg.showRulers()); d->showGuidesAction = new KisAction(i18n("Show Guides"), this); d->showGuidesAction->setCheckable(true); d->showGuidesAction->setCheckable(false); d->showGuidesAction->setActivationFlags(KisAction::ACTIVE_IMAGE); d->showGuidesAction->setToolTip(i18n("Shows or hides guides")); actionManager()->addAction("view_show_guides", d->showGuidesAction); d->zoomTo100pct = new KisAction(i18n("Reset zoom"), this); d->zoomTo100pct->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager()->addAction("zoom_to_100pct", d->zoomTo100pct); d->zoomTo100pct->setDefaultShortcut( QKeySequence( Qt::CTRL + Qt::Key_0 ) ); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); } void KisViewManager::createManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager = new KisFilterManager(this); d->filterManager->setup(actionCollection(), actionManager()); d->selectionManager = new KisSelectionManager(this); d->selectionManager->setup(actionManager()); d->nodeManager = new KisNodeManager(this); d->nodeManager->setup(actionCollection(), actionManager()); d->imageManager = new KisImageManager(this); d->imageManager->setup(actionManager()); d->gridManager = new KisGridManager(this); d->gridManager->setup(this->actionManager()); d->perspectiveGridManager = new KisPerspectiveGridManager(this); d->perspectiveGridManager->setup(actionCollection()); d->paintingAssistantsManager = new KisPaintingAssistantsManager(this); d->paintingAssistantsManager->setup(actionManager()); d->canvasControlsManager = new KisCanvasControlsManager(this); d->canvasControlsManager->setup(actionManager()); d->mirrorManager = new KisMirrorManager(this); d->mirrorManager->setup(actionCollection()); d->inputManager = new KisInputManager(this); } void KisViewManager::updateGUI() { d->guiUpdateCompressor->start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return d->actionManager; } KisPerspectiveGridManager* KisViewManager::perspectiveGridManager() const { return d->perspectiveGridManager; } KisGridManager * KisViewManager::gridManager() const { return d->gridManager; } KisPaintingAssistantsManager* KisViewManager::paintingAssistantsManager() const { return d->paintingAssistantsManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate(KisPart::instance()->templatesResourcePath(), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { if (!document()) return; KisDocument *doc = KisPart::instance()->createDocument(); QString name = document()->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisImageWSP image = document()->image(); KisImageSP newImage = new KisImage(doc->createUndoStore(), image->width(), image->height(), image->colorSpace(), name); newImage->setRootLayer(dynamic_cast(image->rootLayer()->clone().data())); doc->setCurrentImage(newImage); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); KisView *view = KisPart::instance()->createView(doc, resourceProvider()->resourceManager(), mw->actionCollection(), mw); mw->addView(view); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setSaveInBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName)); document()->setSaveInBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setSaveInBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName)); document()->setSaveInBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame->paintopBox()->installEventFilter(&d->blockingEventFilter); foreach(QObject* child, d->controlFrame->paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame->paintopBox()->removeEventFilter(&d->blockingEventFilter); foreach(QObject* child, d->controlFrame->paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { if (d->currentImageView && d->currentImageView->statusBar()) { d->currentImageView->statusBar()->setVisible(toggled); } } void KisViewManager::showJustTheCanvas(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (cfg.hideStatusbarFullscreen()) { if(main->statusBar() && main->statusBar()->isVisible() == toggled) { main->statusBar()->setVisible(!toggled); } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); action->setCheckable(true); if (action && action->isChecked() == toggled) { action->setChecked(!toggled); } } if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); } } if (cfg.hideMenuFullscreen()) { if (main->menuBar()->isVisible() == toggled) { main->menuBar()->setVisible(!toggled); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); foreach(QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_just_the_canvas")->shortcut().toString()), QIcon()); } } void KisViewManager::toggleTabletLogger() { d->inputManager->toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", "krita"); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); foreach(QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::makeStatusBarVisible() { d->mainWindow->statusBar()->setVisible(true); } void KisViewManager::guiUpdateTimeout() { d->nodeManager->updateGUI(); d->selectionManager->updateGUI(); d->filterManager->updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager->updateGUI(); d->perspectiveGridManager->updateGUI(); d->actionManager->updateGUI(); } void KisViewManager::showFloatingMessage(const QString message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_just_the_canvas")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty()) { appAuthorGroup.writeEntry("active-profile", ""); } else if (profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", "anonymous"); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); foreach(KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18n("Default Author Profile")); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); foreach (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous") { d->actionAuthor->setCurrentItem(1); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } else { d->actionAuthor->setCurrentItem(0); } } diff --git a/krita/ui/KisViewManager.h b/krita/ui/KisViewManager.h index 09d3459bc2..d6912ea6be 100644 --- a/krita/ui/KisViewManager.h +++ b/krita/ui/KisViewManager.h @@ -1,248 +1,244 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GUI_CLIENT_H #define KIS_GUI_CLIENT_H #include #include #include #include -#include #include #include #include #include #include "kis_floating_message.h" -#include "KisView.h" -class KisAction; class QPoint; - +class KisView; class KisCanvas2; class KisCanvasResourceProvider; class KisDocument; class KisFilterManager; class KisGridManager; -class KisImage; class KisImageManager; class KisNodeManager; class KisPaintingAssistantsManager; class KisPaintopBox; class KisPerspectiveGridManager; class KisSelectionManager; class KisStatusBar; class KisUndoAdapter; class KisZoomManager; class KisPaintopBox; class KisActionManager; class KisInputManager; /** * Krita view class * * Following the broad model-view-controller idea this class shows you one view on the document. * There can be multiple views of the same document each in with independent settings for viewMode and zoom etc. */ class KRITAUI_EXPORT KisViewManager : public QObject { Q_OBJECT public: /** * Construct a new view on the krita document. * @param document the document we show. * @param parent a parent widget we show ourselves in. */ KisViewManager(QWidget *parent, KActionCollection *actionCollection); virtual ~KisViewManager(); /** * Retrieves the entire action collection. */ virtual KActionCollection* actionCollection() const; public: // Krita specific interfaces void setCurrentView(KisView *view); /// Return the image this view is displaying KisImageWSP image() const; KoZoomController *zoomController() const; /// The resource provider contains all per-view settings, such as /// current color, current paint op etc. KisCanvasResourceProvider * resourceProvider(); /// Return the canvasbase class KisCanvas2 * canvasBase() const; /// Return the actual widget that is displaying the current image QWidget* canvas() const; /// Return the wrapper class around the statusbar KisStatusBar * statusBar() const; /** * This adds a widget to the statusbar for this view. * If you use this method instead of using statusBar() directly, * KisView will take care of removing the items when the view GUI is deactivated * and readding them when it is reactivated. * The parameters are the same as QStatusBar::addWidget(). */ void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false); /** * Remove a widget from the statusbar for this view. */ void removeStatusBarItem(QWidget * widget); KisPaintopBox* paintOpBox() const; /// create a new progress updater KoProgressUpdater *createProgressUpdater(KoProgressUpdater::Mode mode = KoProgressUpdater::Threaded); /// The selection manager handles everything action related to /// selections. KisSelectionManager *selectionManager(); /// The node manager handles everything about nodes KisNodeManager *nodeManager() const; KisActionManager *actionManager() const; /** * Convenience method to get at the active node, which may be * a layer or a mask or a selection */ KisNodeSP activeNode(); /// Convenience method to get at the active layer KisLayerSP activeLayer(); /// Convenience method to get at the active paint device KisPaintDeviceSP activeDevice(); /// The filtermanager handles everything action-related to filters KisFilterManager *filterManager(); /// The image manager handles everything action-related to the /// current image KisImageManager *imageManager(); /// Filters events and sends them to canvas actions KisInputManager *inputManager() const; /// Convenience method to get at the active selection (the /// selection of the current layer, or, if that does not exist, /// the global selection. KisSelectionSP selection(); /// Checks if the current global or local selection is editable bool selectionEditable(); /// The undo adapter is used to add commands to the undo stack KisUndoAdapter *undoAdapter(); KisDocument *document() const; int viewCount() const; public: KisGridManager * gridManager() const; KisPerspectiveGridManager* perspectiveGridManager() const; KisPaintingAssistantsManager* paintingAssistantsManager() const; /// disable and enable toolbar controls. used for disabling them during painting. void enableControls(); void disableControls(); /// shows a floating message in the top right corner of the canvas void showFloatingMessage(const QString message, const QIcon& icon, int timeout = 4500, KisFloatingMessage::Priority priority = KisFloatingMessage::Medium, int alignment = Qt::AlignCenter | Qt::TextWordWrap); /// @return the KoMaindow this view is in, or 0 KisMainWindow *mainWindow() const; /// The QMainWindow associated with this view. This is most likely going to be shell(), but /// when running as Gemini or Sketch, this will be set to the applications' own QMainWindow. /// This can be checked by qobject_casting to KisMainWindow to check the difference. QMainWindow* qtMainWindow() const; /// The mainWindow function will return the shell() value, unless this function is called /// with a non-null value. To make it return shell() again, simply pass null to this function. void setQtMainWindow(QMainWindow* newMainWindow); public Q_SLOTS: void showJustTheCanvas(bool toggled); void setShowFloatingMessage(bool show); void showHideScrollbars(); /// Go to all managers and enable or disable all actions and other /// gui elements void updateGUI(); /// Update the style of all the icons void updateIcons(); void slotViewAdded(KisView *view); void slotViewRemoved(KisView *view); Q_SIGNALS: void floatingMessageRequested(QString message, QString iconName); private Q_SLOTS: void slotBlacklistCleanup(); void slotCreateTemplate(); void slotCreateCopy(); void slotDocumentSaved(); void slotSaveIncremental(); void slotSaveIncrementalBackup(); void showStatusBar(bool toggled); void toggleTabletLogger(); void openResourcesDirectory(); void makeStatusBarVisible(); void guiUpdateTimeout(); void changeAuthorProfile(const QString &profileName); void slotUpdateAuthorProfileActions(); private: void createActions(); void createManagers(); /// The zoommanager handles everything action-related to zooming KisZoomManager * zoomManager(); private: class KisViewManagerPrivate; KisViewManagerPrivate * const d; }; #endif diff --git a/krita/ui/actions/kis_selection_action_factories.cpp b/krita/ui/actions/kis_selection_action_factories.cpp index fc87ba0a5d..37df2dbb11 100644 --- a/krita/ui/actions/kis_selection_action_factories.cpp +++ b/krita/ui/actions/kis_selection_action_factories.cpp @@ -1,536 +1,537 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_action_factories.h" #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisViewManager.h" #include "kis_canvas_resource_provider.h" #include "kis_clipboard.h" #include "kis_pixel_selection.h" #include "kis_paint_layer.h" #include "kis_image.h" #include "kis_image_barrier_locker.h" #include "kis_fill_painter.h" #include "kis_transaction.h" #include "kis_iterator_ng.h" #include "kis_processing_applicator.h" #include "commands/kis_selection_commands.h" #include "commands/kis_image_layer_add_command.h" #include "kis_tool_proxy.h" #include "kis_canvas2.h" #include "kis_canvas_controller.h" #include "kis_selection_manager.h" #include "kis_transaction_based_command.h" #include "kis_selection_filters.h" #include "kis_shape_selection.h" #include "KisPart.h" #include "kis_shape_layer.h" #include #include #include namespace ActionHelper { void copyFromDevice(KisViewManager *view, KisPaintDeviceSP device, bool makeSharpClip = false) { KisImageWSP image = view->image(); if (!image) return; KisSelectionSP selection = view->selection(); QRect rc = (selection) ? selection->selectedExactRect() : image->bounds(); KisPaintDeviceSP clip = new KisPaintDevice(device->colorSpace()); Q_CHECK_PTR(clip); const KoColorSpace *cs = clip->colorSpace(); // TODO if the source is linked... copy from all linked layers?!? // Copy image data KisPainter::copyAreaOptimized(QPoint(), device, clip, rc); if (selection) { // Apply selection mask. KisPaintDeviceSP selectionProjection = selection->projection(); KisHLineIteratorSP layerIt = clip->createHLineIteratorNG(0, 0, rc.width()); KisHLineConstIteratorSP selectionIt = selectionProjection->createHLineIteratorNG(rc.x(), rc.y(), rc.width()); const KoColorSpace *selCs = selection->projection()->colorSpace(); for (qint32 y = 0; y < rc.height(); y++) { for (qint32 x = 0; x < rc.width(); x++) { /** * Sharp method is an exact reverse of COMPOSITE_OVER * so if you cover the cut/copied piece over its source * you get an exactly the same image without any seams */ if (makeSharpClip) { qreal dstAlpha = cs->opacityF(layerIt->rawData()); qreal sel = selCs->opacityF(selectionIt->oldRawData()); qreal newAlpha = sel * dstAlpha / (1.0 - dstAlpha + sel * dstAlpha); float mask = newAlpha / dstAlpha; cs->applyAlphaNormedFloatMask(layerIt->rawData(), &mask, 1); } else { cs->applyAlphaU8Mask(layerIt->rawData(), selectionIt->oldRawData(), 1); } layerIt->nextPixel(); selectionIt->nextPixel(); } layerIt->nextRow(); selectionIt->nextRow(); } } KisClipboard::instance()->setClip(clip, rc.topLeft()); } } void KisSelectAllActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Select All")); if (!image->globalSelection()) { ap->applyCommand(new KisSetEmptyGlobalSelectionCommand(image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } struct SelectAll : public KisTransactionBasedCommand { SelectAll(KisImageSP image) : m_image(image) {} KisImageSP m_image; KUndo2Command* paint() { KisSelectionSP selection = m_image->globalSelection(); KisSelectionTransaction transaction(selection->pixelSelection()); selection->pixelSelection()->select(m_image->bounds()); return transaction.endAndTake(); } }; ap->applyCommand(new SelectAll(image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisDeselectActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; KUndo2Command *cmd = new KisDeselectGlobalSelectionCommand(image); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisReselectActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; KUndo2Command *cmd = new KisReselectGlobalSelectionCommand(image); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisFillActionFactory::run(const QString &fillSource, KisViewManager *view) { KisNodeSP node = view->activeNode(); if (!node || !node->hasEditablePaintDevice()) return; KisSelectionSP selection = view->selection(); QRect selectedRect = selection ? selection->selectedRect() : view->image()->bounds(); Q_UNUSED(selectedRect); KisPaintDeviceSP filled = node->paintDevice()->createCompositionSourceDevice(); Q_UNUSED(filled); bool usePattern = false; bool useBgColor = false; if (fillSource.contains("pattern")) { usePattern = true; } else if (fillSource.contains("bg")) { useBgColor = true; } KisProcessingApplicator applicator(view->image(), node, KisProcessingApplicator::NONE, KisImageSignalVector() << ModifiedSignal, kundo2_i18n("Flood Fill Layer")); KisResourcesSnapshotSP resources = new KisResourcesSnapshot(view->image(), node, 0, view->resourceProvider()->resourceManager()); if (!fillSource.contains("opacity")) { resources->setOpacity(1.0); } KisProcessingVisitorSP visitor = new FillProcessingVisitor(QPoint(0, 0), // start position selection, resources, false, // fast mode usePattern, true, // fill only selection, 0, // feathering radius 0, // sizemod 80, // threshold, false, // unmerged useBgColor); applicator.applyVisitor(visitor, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); applicator.end(); } void KisClearActionFactory::run(KisViewManager *view) { // XXX: "Add saving of XML data for Clear action" view->canvasBase()->toolProxy()->deleteSelection(); } void KisImageResizeToSelectionActionFactory::run(KisViewManager *view) { // XXX: "Add saving of XML data for Image Resize To Selection action" KisSelectionSP selection = view->selection(); if (!selection) return; view->image()->cropImage(selection->selectedExactRect()); } void KisCutCopyActionFactory::run(bool willCut, bool makeSharpClip, KisViewManager *view) { KisImageSP image = view->image(); if (!image) return; bool haveShapesSelected = view->selectionManager()->haveShapesSelected(); if (haveShapesSelected) { // XXX: "Add saving of XML data for Cut/Copy of shapes" KisImageBarrierLocker locker(image); if (willCut) { view->canvasBase()->toolProxy()->cut(); } else { view->canvasBase()->toolProxy()->copy(); } } else { KisNodeSP node = view->activeNode(); if (!node) return; KisSelectionSP selection = view->selection(); if (selection.isNull()) return; { KisImageBarrierLocker locker(image); KisPaintDeviceSP dev = node->paintDevice(); if (!dev) { dev = node->projection(); } if (!dev) { view->showFloatingMessage( i18nc("floating message when cannot copy from a node", "Cannot copy pixels from this type of layer "), QIcon(), 3000, KisFloatingMessage::Medium); return; } if (dev->exactBounds().isEmpty()) { view->showFloatingMessage( i18nc("floating message when copying empty selection", "Selection is empty: no pixels were copied "), QIcon(), 3000, KisFloatingMessage::Medium); return; } ActionHelper::copyFromDevice(view, dev, makeSharpClip); } if (willCut) { KUndo2Command *command = 0; if (willCut && node->hasEditablePaintDevice()) { struct ClearSelection : public KisTransactionBasedCommand { ClearSelection(KisNodeSP node, KisSelectionSP sel) : m_node(node), m_sel(sel) {} KisNodeSP m_node; KisSelectionSP m_sel; KUndo2Command* paint() { KisSelectionSP cutSelection = m_sel; // Shrinking the cutting area was previously used // for getting seamless cut-paste. Now we use makeSharpClip // instead. // QRect originalRect = cutSelection->selectedExactRect(); // static const int preciseSelectionThreshold = 16; // // if (originalRect.width() > preciseSelectionThreshold || // originalRect.height() > preciseSelectionThreshold) { // cutSelection = new KisSelection(*m_sel); // delete cutSelection->flatten(); // // KisSelectionFilter* filter = new KisShrinkSelectionFilter(1, 1, false); // // QRect processingRect = filter->changeRect(originalRect); // filter->process(cutSelection->pixelSelection(), processingRect); // } KisTransaction transaction(m_node->paintDevice()); m_node->paintDevice()->clearSelection(cutSelection); m_node->setDirty(cutSelection->selectedRect()); return transaction.endAndTake(); } }; command = new ClearSelection(node, selection); } KUndo2MagicString actionName = willCut ? kundo2_i18n("Cut") : kundo2_i18n("Copy"); KisProcessingApplicator *ap = beginAction(view, actionName); if (command) { ap->applyCommand(command, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); } KisOperationConfiguration config(id()); config.setProperty("will-cut", willCut); endAction(ap, config.toXML()); } } } void KisCopyMergedActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; image->barrierLock(); KisPaintDeviceSP dev = image->root()->projection(); ActionHelper::copyFromDevice(view, dev); image->unlock(); KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Copy Merged")); endAction(ap, KisOperationConfiguration(id()).toXML()); } void KisPasteActionFactory::run(KisViewManager *view) { KisImageWSP image = view->image(); if (!image) return; KisPaintDeviceSP clip = KisClipboard::instance()->clip(image->bounds(), true); if (clip) { KisPaintLayer *newLayer = new KisPaintLayer(image.data(), image->nextLayerName() + i18n("(pasted)"), OPACITY_OPAQUE_U8, clip); KisNodeSP aboveNode = view->activeLayer(); KisNodeSP parentNode = aboveNode ? aboveNode->parent() : image->root(); KUndo2Command *cmd = new KisImageLayerAddCommand(image, newLayer, parentNode, aboveNode); KisProcessingApplicator *ap = beginAction(view, cmd->text()); ap->applyCommand(cmd, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::NORMAL); endAction(ap, KisOperationConfiguration(id()).toXML()); } else { // XXX: "Add saving of XML data for Paste of shapes" view->canvasBase()->toolProxy()->paste(); } } void KisPasteNewActionFactory::run(KisViewManager *viewManager) { Q_UNUSED(viewManager); KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true); if (!clip) return; QRect rect = clip->exactBounds(); if (rect.isEmpty()) return; KisDocument *doc = KisPart::instance()->createDocument(); KisImageSP image = new KisImage(doc->createUndoStore(), rect.width(), rect.height(), clip->colorSpace(), i18n("Pasted")); KisPaintLayerSP layer = new KisPaintLayer(image.data(), clip->objectName(), OPACITY_OPAQUE_U8, clip->colorSpace()); KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect); image->addNode(layer.data(), image->rootLayer()); doc->setCurrentImage(image); KisPart::instance()->addDocument(doc); KisMainWindow *win = viewManager->mainWindow(); KisView *view = KisPart::instance()->createView(doc, win->resourceManager(), win->actionCollection(), win); win->addView(view); } void KisInvertSelectionOperaton::runFromXML(KisViewManager* view, const KisOperationConfiguration& config) { KisSelectionFilter* filter = new KisInvertSelectionFilter(); runFilter(filter, view, config); } void KisSelectionToVectorActionFactory::run(KisViewManager *view) { KisSelectionSP selection = view->selection(); if (selection->hasShapeSelection() || !selection->outlineCacheValid()) { return; } QPainterPath selectionOutline = selection->outlineCache(); QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform(); KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline)); shape->setShapeId(KoPathShapeId); /** * Mark a shape that it belongs to a shape selection */ if(!shape->userData()) { shape->setUserData(new KisShapeSelectionMarker); } KisProcessingApplicator *ap = beginAction(view, kundo2_i18n("Convert to Vector Selection")); ap->applyCommand(view->canvasBase()->shapeController()->addShape(shape), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); endAction(ap, KisOperationConfiguration(id()).toXML()); } class KisShapeSelectionPaste : public KoOdfPaste { public: KisShapeSelectionPaste(KisViewManager* view) : m_view(view) { } virtual ~KisShapeSelectionPaste() { } virtual bool process(const KoXmlElement & body, KoOdfReadStore & odfStore) { KoOdfLoadingContext loadingContext(odfStore.styles(), odfStore.store()); KoShapeLoadingContext context(loadingContext, m_view->canvasBase()->shapeController()->resourceManager()); KoXmlElement child; QList shapes; forEachElement(child, body) { KoShape * shape = KoShapeRegistry::instance()->createShapeFromOdf(child, context); if (shape) { shapes.append(shape); } } if (!shapes.isEmpty()) { KisSelectionToolHelper helper(m_view->canvasBase(), kundo2_i18n("Convert shapes to vector selection")); helper.addSelectionShapes(shapes); } return true; } private: KisViewManager* m_view; }; void KisShapesToVectorSelectionActionFactory::run(KisViewManager* view) { QList shapes = view->canvasBase()->shapeManager()->selection()->selectedShapes(); KoShapeOdfSaveHelper saveHelper(shapes); KoDrag drag; drag.setOdf(KoOdf::mimeType(KoOdf::Text), saveHelper); QMimeData* mimeData = drag.mimeData(); Q_ASSERT(mimeData->hasFormat(KoOdf::mimeType(KoOdf::Text))); KisShapeSelectionPaste paste(view); paste.paste(KoOdf::Text, mimeData); } void KisSelectionToShapeActionFactory::run(KisViewManager *view) { KisSelectionSP selection = view->selection(); if (!selection->outlineCacheValid()) { return; } QPainterPath selectionOutline = selection->outlineCache(); QTransform transform = view->canvasBase()->coordinatesConverter()->imageToDocumentTransform(); KoShape *shape = KoPathShape::createShapeFromPainterPath(transform.map(selectionOutline)); shape->setShapeId(KoPathShapeId); KoColor fgColor = view->canvasBase()->resourceManager()->resource(KoCanvasResourceManager::ForegroundColor).value(); KoShapeStroke* border = new KoShapeStroke(1.0, fgColor.toQColor()); shape->setStroke(border); view->document()->shapeController()->addShape(shape); } diff --git a/krita/ui/canvas/kis_abstract_canvas_widget.h b/krita/ui/canvas/kis_abstract_canvas_widget.h index 320aa889dc..0b953ef933 100644 --- a/krita/ui/canvas/kis_abstract_canvas_widget.h +++ b/krita/ui/canvas/kis_abstract_canvas_widget.h @@ -1,95 +1,94 @@ /* * Copyright (C) 2007 Boudewijn Rempt , (C) * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_ABSTRACT_CANVAS_WIDGET_ #define _KIS_ABSTRACT_CANVAS_WIDGET_ class QWidget; class QRect; class QPainter; class QRect; class KoToolProxy; -class KisCanvas2; class KisCanvasDecoration; class KisDisplayFilter; class KisDisplayColorConverter; class QBitArray; #include "kis_types.h" #include "kis_ui_types.h" class KisAbstractCanvasWidget { public: KisAbstractCanvasWidget() {} virtual ~KisAbstractCanvasWidget() {} virtual QWidget * widget() = 0; virtual KoToolProxy * toolProxy() const = 0; /// Draw the specified decorations on the view. virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const = 0; virtual void addDecoration(KisCanvasDecoration* deco) = 0; virtual KisCanvasDecoration* decoration(const QString& id) const = 0; virtual void setDecorations(const QList &) = 0; virtual QList decorations() const = 0; /// set the specified display filter on the canvas virtual void setDisplayFilter(KisDisplayFilter *displayFilter) = 0; virtual void setWrapAroundViewingMode(bool value) = 0; // Called from KisCanvas2::channelSelectionChanged virtual void channelSelectionChanged(QBitArray channelFlags) = 0; // Called from KisCanvas2::slotSetDisplayProfile virtual void setDisplayProfile(KisDisplayColorConverter *colorConverter) = 0; // Called from KisCanvas2::disconnectCurrentCanvas virtual void disconnectCurrentCanvas() = 0; // Called from KisCanvas2::finishResizingImage virtual void finishResizingImage(qint32 w, qint32 h) = 0; // Called from KisCanvas2::startUpdateProjection virtual KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags) = 0; // Called from KisCanvas2::updateCanvasProjection virtual QRect updateCanvasProjection(KisUpdateInfoSP info) = 0; /** * Returns true if the asynchromous engine of the canvas * (e.g. openGL pipeline) is busy with processing of the previous * update events. This will make KisCanvas2 to postpone and * compress update events. */ virtual bool isBusy() const = 0; }; #endif // _KIS_ABSTRACT_CANVAS_WIDGET_ diff --git a/krita/ui/canvas/kis_canvas_decoration.cc b/krita/ui/canvas/kis_canvas_decoration.cc index 12f083c609..5d2d6bf9ce 100644 --- a/krita/ui/canvas/kis_canvas_decoration.cc +++ b/krita/ui/canvas/kis_canvas_decoration.cc @@ -1,94 +1,92 @@ /* * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_canvas_decoration.h" -#include "KisView.h" -#include "kis_canvas2.h" #include "kis_debug.h" struct KisCanvasDecoration::Private { bool visible; QPointer view; QString id; }; KisCanvasDecoration::KisCanvasDecoration(const QString& id, QPointerparent) : QObject(parent) , d(new Private) { d->visible = false; d->view = parent; d->id = id; } KisCanvasDecoration::~KisCanvasDecoration() { delete d; } void KisCanvasDecoration::setView(QPointerimageView) { d->view = imageView; } const QString& KisCanvasDecoration::id() const { return d->id; } void KisCanvasDecoration::setVisible(bool v) { d->visible = v; if (d->view && d->view->canvasBase()) { d->view->canvasBase()->canvasWidget()->update(); } } bool KisCanvasDecoration::visible() const { return d->visible; } void KisCanvasDecoration::toggleVisibility() { setVisible(!visible()); } void KisCanvasDecoration::paint(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter, KisCanvas2 *canvas = 0) { if (!canvas) { dbgFile<<"canvas does not exist:"<KisCanvasDecoration::imageView() { return d->view; } QPointerKisCanvasDecoration::view() const { return d->view; } diff --git a/krita/ui/canvas/kis_canvas_decoration.h b/krita/ui/canvas/kis_canvas_decoration.h index 406c166b96..264ce2ddd5 100644 --- a/krita/ui/canvas/kis_canvas_decoration.h +++ b/krita/ui/canvas/kis_canvas_decoration.h @@ -1,88 +1,86 @@ /* * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CANVAS_DECORATION_H_ #define _KIS_CANVAS_DECORATION_H_ #include #include #include #include #include #include -class QPoint; -class QRect; class QRectF; class QPainter; class KisCoordinatesConverter; /** * This class is the base class for object that draw a decoration on the canvas, * for instance, selections, grids, tools, ... */ class KRITAUI_EXPORT KisCanvasDecoration : public QObject { Q_OBJECT public: KisCanvasDecoration(const QString& id, QPointerparent); ~KisCanvasDecoration(); void setView(QPointer imageView); const QString& id() const; /** * @return whether the decoration is visible. */ bool visible() const; /** * Will paint the decoration on the QPainter, if the visible is set to true. * * @param updateRect dirty rect in document pixels */ void paint(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas); public Q_SLOTS: /** * Set if the decoration is visible or not. */ virtual void setVisible(bool v); /** * If decoration is visible, hide it, if not show it. */ void toggleVisibility(); protected: virtual void drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter,KisCanvas2* canvas) = 0; /// XXX: unify view and imageview! QPointerimageView(); /** * @return the parent KisView */ QPointer view() const; private: struct Private; Private* const d; }; #endif diff --git a/krita/ui/canvas/kis_canvas_widget_base.cpp b/krita/ui/canvas/kis_canvas_widget_base.cpp index 190078d445..b7c1c6eafa 100644 --- a/krita/ui/canvas/kis_canvas_widget_base.cpp +++ b/krita/ui/canvas/kis_canvas_widget_base.cpp @@ -1,248 +1,249 @@ /* * Copyright (C) 2007, 2010 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_canvas_widget_base.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_coordinates_converter.h" #include "kis_canvas_decoration.h" #include "../kis_config.h" #include "kis_canvas2.h" #include "../KisViewManager.h" #include "../kis_selection_manager.h" +#include "../KisDocument.h" struct KisCanvasWidgetBase::Private { public: Private(KisCanvas2 *newCanvas, KisCoordinatesConverter *newCoordinatesConverter) : canvas(newCanvas) , coordinatesConverter(newCoordinatesConverter) , viewConverter(newCanvas->viewConverter()) , toolProxy(newCanvas->toolProxy()) , ignorenextMouseEventExceptRightMiddleClick(0) , borderColor(Qt::gray) {} QList decorations; KisCanvas2 * canvas; KisCoordinatesConverter *coordinatesConverter; const KoViewConverter * viewConverter; KoToolProxy * toolProxy; QTimer blockMouseEvent; bool ignorenextMouseEventExceptRightMiddleClick; // HACK work around Qt bug not sending tablet right/dblclick http://bugreports.qt.nokia.com/browse/QTBUG-8598 QColor borderColor; }; KisCanvasWidgetBase::KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter) : m_d(new Private(canvas, coordinatesConverter)) { m_d->blockMouseEvent.setSingleShot(true); } KisCanvasWidgetBase::~KisCanvasWidgetBase() { delete m_d; } void KisCanvasWidgetBase::drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const { gc.save(); if (!m_d->canvas) { dbgFile<<"canvas doesn't exist, in canvas widget base!"; } // Setup the painter to take care of the offset; all that the // classes that do painting need to keep track of is resolution gc.setRenderHint(QPainter::Antialiasing); gc.setRenderHint(QPainter::TextAntialiasing); // This option does not do anything anymore with Qt4.6, so don't reenable it since it seems to break display // http://www.archivum.info/qt-interest@trolltech.com/2010-01/00481/Re:-(Qt-interest)-Is-QPainter::HighQualityAntialiasing-render-hint-broken-in-Qt-4.6.html // gc.setRenderHint(QPainter::HighQualityAntialiasing); gc.setRenderHint(QPainter::SmoothPixmapTransform); gc.save(); gc.setClipRect(updateWidgetRect); QTransform transform = m_d->coordinatesConverter->flakeToWidgetTransform(); gc.setTransform(transform); // Paint the shapes (other than the layers) m_d->canvas->globalShapeManager()->paint(gc, *m_d->viewConverter, false); // draw green selection outlines around text shapes that are edited, so the user sees where they end gc.save(); QTransform worldTransform = gc.worldTransform(); gc.setPen( Qt::green ); foreach (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) { if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") { gc.setWorldTransform(shape->absoluteTransformation(m_d->viewConverter) * worldTransform); KoShape::applyConversion(gc, *m_d->viewConverter); gc.drawRect(QRectF(QPointF(), shape->size())); } } gc.restore(); // Draw text shape over canvas while editing it, that's needs to show the text selection correctly QString toolId = KoToolManager::instance()->activeToolId(); if (toolId == "ArtisticTextToolFactoryID" || toolId == "TextToolFactory_ID") { gc.save(); gc.setPen(Qt::NoPen); gc.setBrush(Qt::NoBrush); foreach (KoShape *shape, canvas()->shapeManager()->selection()->selectedShapes()) { if (shape->shapeId() == "ArtisticText" || shape->shapeId() == "TextShapeID") { KoShapePaintingContext paintContext(canvas(), false); gc.save(); gc.setTransform(shape->absoluteTransformation(m_d->viewConverter) * gc.transform()); canvas()->shapeManager()->paintShape(shape, gc, *m_d->viewConverter, paintContext); gc.restore(); } } gc.restore(); } // - some tools do not restore gc, but that is not important here // - we need to disable clipping to draw handles properly gc.setClipping(false); toolProxy()->paint(gc, *m_d->viewConverter); gc.restore(); // ask the decorations to paint themselves foreach(KisCanvasDecoration* deco, m_d->decorations) { deco->paint(gc, m_d->coordinatesConverter->widgetToDocument(updateWidgetRect), m_d->coordinatesConverter,m_d->canvas); } // then paint the guides if (m_d->canvas && m_d->canvas->viewManager() && m_d->canvas->viewManager()->document()) { m_d->canvas->viewManager()->document()->guidesData().paintGuides(gc, *m_d->viewConverter, updateWidgetRect); } gc.restore(); } void KisCanvasWidgetBase::addDecoration(KisCanvasDecoration* deco) { m_d->decorations.push_back(deco); } KisCanvasDecoration* KisCanvasWidgetBase::decoration(const QString& id) const { foreach(KisCanvasDecoration* deco, m_d->decorations) { if (deco->id() == id) { return deco; } } return 0; } void KisCanvasWidgetBase::setDecorations(const QList &decorations) { m_d->decorations=decorations; } QList KisCanvasWidgetBase::decorations() const { return m_d->decorations; } void KisCanvasWidgetBase::setWrapAroundViewingMode(bool value) { Q_UNUSED(value); } QImage KisCanvasWidgetBase::createCheckersImage(qint32 checkSize) { KisConfig cfg; if(checkSize < 0) checkSize = cfg.checkSize(); QColor checkColor1 = cfg.checkersColor1(); QColor checkColor2 = cfg.checkersColor2(); QImage tile(checkSize * 2, checkSize * 2, QImage::Format_RGB32); QPainter pt(&tile); pt.fillRect(tile.rect(), checkColor2); pt.fillRect(0, 0, checkSize, checkSize, checkColor1); pt.fillRect(checkSize, checkSize, checkSize, checkSize, checkColor1); pt.end(); return tile; } void KisCanvasWidgetBase::notifyConfigChanged() { KisConfig cfg; m_d->borderColor = cfg.canvasBorderColor(); } QColor KisCanvasWidgetBase::borderColor() const { return m_d->borderColor; } KisCanvas2 *KisCanvasWidgetBase::canvas() const { return m_d->canvas; } KisCoordinatesConverter* KisCanvasWidgetBase::coordinatesConverter() const { return m_d->coordinatesConverter; } KoToolProxy *KisCanvasWidgetBase::toolProxy() const { return m_d->toolProxy; } void KisCanvasWidgetBase::setDisplayFilter(KisDisplayFilter */*displayFilter*/) { } QVariant KisCanvasWidgetBase::processInputMethodQuery(Qt::InputMethodQuery query) const { if (query == Qt::ImMicroFocus) { QRectF rect = m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter).toRectF(); return m_d->coordinatesConverter->flakeToWidget(rect); } return m_d->toolProxy->inputMethodQuery(query, *m_d->viewConverter); } void KisCanvasWidgetBase::processInputMethodEvent(QInputMethodEvent *event) { m_d->toolProxy->inputMethodEvent(event); } diff --git a/krita/ui/canvas/kis_canvas_widget_base.h b/krita/ui/canvas/kis_canvas_widget_base.h index 8806a61adc..7eab7f0fd4 100644 --- a/krita/ui/canvas/kis_canvas_widget_base.h +++ b/krita/ui/canvas/kis_canvas_widget_base.h @@ -1,101 +1,102 @@ /* * Copyright (C) 2007 Boudewijn Rempt , (C) * Copyright (C) 2010 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_CANVAS_WIDGET_BASE_ #define _KIS_CANVAS_WIDGET_BASE_ #include #include #include class QColor; class QImage; class QInputMethodEvent; class QVariant; class KisCoordinatesConverter; class KisDisplayFilter; +class KisCanvas2; #include "kritaui_export.h" class KRITAUI_EXPORT KisCanvasWidgetBase : public KisAbstractCanvasWidget { public: KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter); virtual ~KisCanvasWidgetBase(); public: // KisAbstractCanvasWidget virtual KoToolProxy *toolProxy() const; /// set the specified display filter on the canvas virtual void setDisplayFilter(KisDisplayFilter *displayFilter); /** * Draw the specified decorations on the view. */ virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const; virtual void addDecoration(KisCanvasDecoration* deco); virtual KisCanvasDecoration* decoration(const QString& id) const; virtual void setDecorations(const QList &); virtual QList decorations() const; virtual void setWrapAroundViewingMode(bool value); /** * Returns the color of the border, i.e. the part of the canvas * outside the image contents. * */ QColor borderColor() const; /** * Returns one check of the background checkerboard pattern. */ static QImage createCheckersImage(qint32 checkSize = -1); KisCoordinatesConverter* coordinatesConverter() const; protected: KisCanvas2 *canvas() const; /** * Event handlers to be called by derived canvas event handlers. * All common event processing is carried out by these * functions. */ QVariant processInputMethodQuery(Qt::InputMethodQuery query) const; void processInputMethodEvent(QInputMethodEvent *event); void notifyConfigChanged(); /// To be implemented by the derived canvas virtual bool callFocusNextPrevChild(bool next) = 0; private: struct Private; Private * const m_d; }; #endif // _KIS_CANVAS_WIDGET_BASE_ diff --git a/krita/ui/canvas/kis_display_filter.h b/krita/ui/canvas/kis_display_filter.h index 39ea7b09d6..8971c62b97 100644 --- a/krita/ui/canvas/kis_display_filter.h +++ b/krita/ui/canvas/kis_display_filter.h @@ -1,55 +1,54 @@ /* * Copyright (c) 2012 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DISPLAY_FILTER_H #define KIS_DISPLAY_FILTER_H #include #include #include #include #include -#include "kis_exposure_gamma_correction_interface.h" - +class KisExposureGammaCorrectionInterface; /** * @brief The KisDisplayFilter class is the base class for filters that * are applied by the canvas to the projection before displaying. */ class KRITAUI_EXPORT KisDisplayFilter : public QObject { Q_OBJECT public: explicit KisDisplayFilter(QObject *parent = 0); #ifdef HAVE_OPENGL virtual QString program() const = 0; virtual GLuint lutTexture() const = 0; #endif virtual void filter(quint8 *pixels, quint32 numPixels) = 0; virtual void approximateInverseTransformation(quint8 *pixels, quint32 numPixels) = 0; virtual void approximateForwardTransformation(quint8 *pixels, quint32 numPixels) = 0; virtual bool useInternalColorManagement() const = 0; virtual KisExposureGammaCorrectionInterface *correctionInterface() const = 0; virtual bool lockCurrentColorVisualRepresentation() const = 0; }; #endif diff --git a/krita/ui/canvas/kis_grid_manager.cpp b/krita/ui/canvas/kis_grid_manager.cpp index 953baa7c41..33c1bb8839 100644 --- a/krita/ui/canvas/kis_grid_manager.cpp +++ b/krita/ui/canvas/kis_grid_manager.cpp @@ -1,103 +1,102 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_grid_manager.h" #include #include #include #include #include #include "kis_coordinates_converter.h" #include "kis_config.h" #include "kis_grid_painter_configuration.h" #include "kis_grid_decoration.h" #include "kis_image.h" #include "KisViewManager.h" #include "KisDocument.h" #include "KisView.h" -#include "kis_action.h" KisGridManager::KisGridManager(KisViewManager * parent) : QObject(parent) { } KisGridManager::~KisGridManager() { } void KisGridManager::setup(KisActionManager* actionManager) { m_toggleGrid = new KisAction(KisIconUtils::loadIcon("view-grid"), i18n("Show Grid"), 0); m_toggleGrid->setCheckable(true); m_toggleGrid->setActivationFlags(KisAction::ACTIVE_NODE); m_toggleGrid->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Apostrophe)); actionManager->addAction("view_grid", m_toggleGrid); m_toggleSnapToGrid = new KisAction(i18n("Snap To Grid"), this); m_toggleSnapToGrid->setCheckable(true); m_toggleSnapToGrid->setActivationFlags(KisAction::ACTIVE_NODE); m_toggleSnapToGrid->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Semicolon)); //actionManager->addAction("view_snap_to_grid", m_toggleSnapToGrid); connect(m_toggleSnapToGrid, SIGNAL(triggered()), this, SLOT(toggleSnapToGrid())); } void KisGridManager::updateGUI() { } void KisGridManager::setView(QPointer imageView) { if (m_imageView) { m_toggleGrid->disconnect(); m_gridDecoration = 0; } m_imageView = imageView; if (imageView) { m_gridDecoration = qobject_cast(imageView->canvasBase()->decoration("grid")); if (!m_gridDecoration) { m_gridDecoration = new KisGridDecoration(imageView); imageView->canvasBase()->addDecoration(m_gridDecoration); } checkVisibilityAction(m_gridDecoration->visible()); connect(m_toggleGrid, SIGNAL(triggered()), m_gridDecoration, SLOT(toggleVisibility())); } } void KisGridManager::checkVisibilityAction(bool check) { m_toggleGrid->setChecked(check); } void KisGridManager::toggleSnapToGrid() { if (m_imageView) { m_imageView->document()->gridData().setSnapToGrid(m_toggleSnapToGrid->isChecked()); m_imageView->canvasBase()->updateCanvas(); } } diff --git a/krita/ui/canvas/kis_grid_manager.h b/krita/ui/canvas/kis_grid_manager.h index fb90d3a473..21503fd0d5 100644 --- a/krita/ui/canvas/kis_grid_manager.h +++ b/krita/ui/canvas/kis_grid_manager.h @@ -1,67 +1,66 @@ /* * This file is part of Krita * * Copyright (c) 2006 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GRID_MANAGER_H #define KIS_GRID_MANAGER_H -#include "kis_canvas_decoration.h" #include #include "kis_types.h" #include #include "kis_action_manager.h" #include "kis_action.h" class KisGridDecoration; class KisViewManager; class KRITAUI_EXPORT KisGridManager : public QObject { Q_OBJECT public: KisGridManager(KisViewManager * parent); virtual ~KisGridManager(); public: void setup(KisActionManager * actionManager); void setView(QPointerimageView); public Q_SLOTS: void updateGUI(); void checkVisibilityAction(bool check); private Q_SLOTS: void toggleSnapToGrid(); private: void setFastConfig(int size); KisAction *m_toggleGrid; KisAction* m_toggleSnapToGrid; QPointer m_imageView; KisGridDecoration* m_gridDecoration; }; #endif diff --git a/krita/ui/canvas/kis_perspective_grid_manager.h b/krita/ui/canvas/kis_perspective_grid_manager.h index 757a855e06..e7f599ab0b 100644 --- a/krita/ui/canvas/kis_perspective_grid_manager.h +++ b/krita/ui/canvas/kis_perspective_grid_manager.h @@ -1,71 +1,72 @@ /* * This file is part of Krita * * Copyright (c) 2006,2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PERSPECTIVE_GRID_MANAGER_H #define KIS_PERSPECTIVE_GRID_MANAGER_H -#include "kis_canvas_decoration.h" #include +#include +#include "KisView.h" #include class KisPerspectiveGridDecoration; class QAction; class KActionCollection; class KToggleAction; class KisViewManager; class KRITAUI_EXPORT KisPerspectiveGridManager : public QObject { Q_OBJECT public: /** Create a perspective manager for this view */ KisPerspectiveGridManager(KisViewManager * parent); ~KisPerspectiveGridManager(); void setup(KActionCollection * collection); /** * Call this function to start editing the grid, to disable display */ void startEdition(); /** * Call this function when the edition of the grid is finished. Trigger a redisplay of the perspective * grid if necesserary */ void stopEdition(); void setView(QPointer imageView); public Q_SLOTS: void updateGUI(); /** * Call this to remove all the perspective subgrids. */ void clearPerspectiveGrid(); private: KisPerspectiveGridDecoration* decoration(); QPointer m_imageView; KToggleAction* m_toggleGrid; QAction * m_gridClear; }; #endif diff --git a/krita/ui/canvas/kis_prescaled_projection.h b/krita/ui/canvas/kis_prescaled_projection.h index 92d1dc53a5..7dd273cc96 100644 --- a/krita/ui/canvas/kis_prescaled_projection.h +++ b/krita/ui/canvas/kis_prescaled_projection.h @@ -1,182 +1,180 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PRESCALED_PROJECTION_H #define KIS_PRESCALED_PROJECTION_H #include #include #include -#include "kis_update_info.h" #include "KoColorConversionTransformation.h" class QImage; -class QPoint; class QRect; class QSize; class QPainter; class KoColorProfile; class KisCoordinatesConverter; class KisDisplayFilter; #include #include "kis_ui_types.h" /** * KisPrescaledProjection is responsible for keeping around a * prescaled QImage representation that is always suitable for * painting onto the canvas. * * Note: the export macro is only for the unittest. */ class KRITAUI_EXPORT KisPrescaledProjection : public QObject, public KisShared { Q_OBJECT public: KisPrescaledProjection(); virtual ~KisPrescaledProjection(); void setImage(KisImageWSP image); /** * Return the prescaled QImage. The prescaled image is exactly as big as * the canvas widget in pixels. */ QImage prescaledQImage() const; void setCoordinatesConverter(KisCoordinatesConverter *coordinatesConverter); public Q_SLOTS: /** * Retrieves image's data from KisImage object and updates * internal cache * @param dirtyImageRect the rect changed on the image * @see recalculateCache */ KisUpdateInfoSP updateCache(const QRect &dirtyImageRect); /** * Updates the prescaled cache at current zoom level * @param info update structure returned by updateCache * @see updateCache */ void recalculateCache(KisUpdateInfoSP info); /** * Called whenever the configuration settings change. */ void updateSettings(); /** * Called whenever the view widget needs to show a different part of * the document */ void viewportMoved(const QPointF &offset); /** * Called whenever the size of the KisImage changes. * It is a part of a complex update ritual, when the size * fo the image changes. This method just resizes the storage * for the image cache, it doesn't update any cached data. */ void slotImageSizeChanged(qint32 w, qint32 h); /** * Checks whether it is needed to resize the prescaled image and * updates it. The size is given in canvas widget pixels. */ void notifyCanvasSizeChanged(const QSize &widgetSize); void notifyZoomChanged(); /** * Set the current monitor profile */ void setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); void setChannelFlags(const QBitArray &channelFlags); void setDisplayFilter(KisDisplayFilter *displayFilter); /** * Called whenever the zoom level changes or another chunk of the * image becomes visible. The currently visible area of the image * is complete scaled again. */ void preScale(); private: friend class KisPrescaledProjectionTest; KisPrescaledProjection(const KisPrescaledProjection &); KisPrescaledProjection operator=(const KisPrescaledProjection &); void updateViewportSize(); /** * This creates an empty update information and fills it with the only * parameter: @p dirtyImageRect * This function is supposed to be run in the context of the image * threads, so it does no accesses to zoom or any UI specific values. * All the needed information for zooming will be fetched in the context * of the UI thread in fillInUpdateInformation(). * * @see fillInUpdateInformation() */ KisPPUpdateInfoSP getInitialUpdateInformation(const QRect &dirtyImageRect); /** * Prepare all the information about rects needed during * projection updating. * * @param viewportRect the part of the viewport that has to be updated * @param info the structure to be filled in. It's member dirtyImageRect * is supposed to have already been set up in the previous step of the * update in getInitialUpdateInformation(). Though it is allowed to * be null rect. * * @see getInitialUpdateInformation() */ void fillInUpdateInformation(const QRect &viewportRect, KisPPUpdateInfoSP info); /** * Initiates the process of prescaled image update * * @param info prepared information */ void updateScaledImage(KisPPUpdateInfoSP info); /** * Atual drawing is done here * @param info prepared information * @param gc The painter we draw on */ void drawUsingBackend(QPainter &gc, KisPPUpdateInfoSP info); struct Private; Private * const m_d; }; #endif diff --git a/krita/ui/canvas/kis_qpainter_canvas.h b/krita/ui/canvas/kis_qpainter_canvas.h index de13709d56..93383b7a41 100644 --- a/krita/ui/canvas/kis_qpainter_canvas.h +++ b/krita/ui/canvas/kis_qpainter_canvas.h @@ -1,98 +1,96 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * Copyright (C) Lukáš Tvrdý , (C) 2009 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_QPAINTER_CANVAS_H #define KIS_QPAINTER_CANVAS_H #include #include "kis_canvas_widget_base.h" -#include "kis_prescaled_projection.h" #include "kis_ui_types.h" class QImage; class QPaintEvent; -class QPainter; class KisCanvas2; class KisDisplayColorConverter; /** * * KisQPainterCanvas is the widget that shows the actual image using arthur. * * NOTE: if you change something in the event handling here, also change it * in the opengl canvas. * * @author Boudewijn Rempt */ class KisQPainterCanvas : public QWidget, public KisCanvasWidgetBase { Q_OBJECT public: KisQPainterCanvas(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent); virtual ~KisQPainterCanvas(); void setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection); public: // QWidget overrides void paintEvent(QPaintEvent * ev); void resizeEvent(QResizeEvent *e); virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; virtual void inputMethodEvent(QInputMethodEvent *event); public: // Implement kis_abstract_canvas_widget interface void setDisplayFilter(KisDisplayFilter* displayFilter); void setWrapAroundViewingMode(bool value); void channelSelectionChanged(QBitArray channelFlags); void setDisplayProfile(KisDisplayColorConverter *colorConverter); void disconnectCurrentCanvas(); void finishResizingImage(qint32 w, qint32 h); KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags); QRect updateCanvasProjection(KisUpdateInfoSP info); QWidget * widget() { return this; } bool isBusy() const { return false; } protected: // KisCanvasWidgetBase virtual bool callFocusNextPrevChild(bool next); private Q_SLOTS: void slotConfigChanged(); private: QImage m_buffer; class Private; Private * const m_d; }; #endif diff --git a/krita/ui/dialogs/kis_dlg_adjustment_layer.h b/krita/ui/dialogs/kis_dlg_adjustment_layer.h index 44f43ab8b5..0a610f919d 100644 --- a/krita/ui/dialogs/kis_dlg_adjustment_layer.h +++ b/krita/ui/dialogs/kis_dlg_adjustment_layer.h @@ -1,81 +1,79 @@ /* * Copyright (c) 2006 Boudewijn Rempt * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISDLGAdjustMENTLAYER_H #define KISDLGAdjustMENTLAYER_H #include #include -class KisFilter; -class QLabel; class KisFilterConfiguration; class KisNodeFilterInterface; class KisViewManager; #include "kis_types.h" #include "ui_wdgfilternodecreation.h" /** * Create a new adjustment layer. */ class KisDlgAdjustmentLayer : public KoDialog { Q_OBJECT public: /** * Create a new adjustmentlayer dialog * * @param layerName the name of the adjustment layer * @param paintDevice the paint device that is used as source for the preview * @param caption the caption for the dialog -- create or properties * @param parent the widget parent of this dialog * @param name the QObject name, if any */ KisDlgAdjustmentLayer(KisNodeSP node, KisNodeFilterInterface* nfi, KisPaintDeviceSP paintDevice, const QString & layerName, const QString & caption, KisViewManager *view, QWidget *parent = 0); ~KisDlgAdjustmentLayer(); KisFilterConfiguration * filterConfiguration() const; QString layerName() const; public Q_SLOTS: void adjustSize(); protected Q_SLOTS: void slotNameChanged(const QString &); void slotConfigChanged(); void slotFilterWidgetSizeChanged(); private: KisNodeSP m_node; KisNodeFilterInterface *m_nodeFilterInterface; Ui::WdgFilterNodeCreation wdgFilterNodeCreation; KisFilterConfiguration *m_currentFilter; bool m_customName; QString m_layerName; }; #endif diff --git a/krita/ui/dialogs/kis_dlg_filter.h b/krita/ui/dialogs/kis_dlg_filter.h index 7b82601322..7bf9515f0d 100644 --- a/krita/ui/dialogs/kis_dlg_filter.h +++ b/krita/ui/dialogs/kis_dlg_filter.h @@ -1,73 +1,72 @@ /* * Copyright (c) 2007 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KIS_DLG_FILTER_H_ #define _KIS_DLG_FILTER_H_ #include #include -class KisFilter; class KisViewManager; class KisFilterManager; class KisDlgFilter : public QDialog { Q_OBJECT public: KisDlgFilter(KisViewManager *view, KisNodeSP node, KisFilterManager *filterManager, QWidget *parent = 0); ~KisDlgFilter(); void setFilter(KisFilterSP f); protected Q_SLOTS: void slotOnAccept(); void slotOnReject(); void createMask(); void enablePreviewToggled(bool state); void filterSelectionChanged(); virtual void resizeEvent(QResizeEvent* ); public Q_SLOTS: void adjustSize(); private: void startApplyingFilter(KisSafeFilterConfigurationSP config); void setDialogTitle(KisFilterSP f); void updatePreview(); private Q_SLOTS: void slotFilterWidgetSizeChanged(); private: struct Private; KisDlgFilter::Private* const d; }; #endif diff --git a/krita/ui/dialogs/kis_dlg_generator_layer.h b/krita/ui/dialogs/kis_dlg_generator_layer.h index af8ed893b7..2e6125fc96 100644 --- a/krita/ui/dialogs/kis_dlg_generator_layer.h +++ b/krita/ui/dialogs/kis_dlg_generator_layer.h @@ -1,64 +1,63 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_DLG_GENERATORLAYER_H #define KIS_DLG_GENERATORLAYER_H #include #include -class KisFilter; class KisFilterConfiguration; class KisViewManager; #include "ui_wdgdlggeneratorlayer.h" #include /** * Create a new generator layer */ class KisDlgGeneratorLayer : public KoDialog { public: Q_OBJECT public: /** * Create a new generator layer * @param name the proposed name for this layer * @param parent the widget parent of this dialog */ KisDlgGeneratorLayer(const QString & name, KisViewManager *view, QWidget *parent); void setConfiguration(const KisFilterConfiguration * config); KisFilterConfiguration * configuration() const; QString layerName() const; protected Q_SLOTS: void slotNameChanged(const QString &); private: Ui_WdgDlgGeneratorLayer dlgWidget; bool m_customName; bool m_freezeName; }; #endif diff --git a/krita/ui/dialogs/kis_dlg_image_properties.cc b/krita/ui/dialogs/kis_dlg_image_properties.cc index c3bf8e80bd..e1d7f1de29 100644 --- a/krita/ui/dialogs/kis_dlg_image_properties.cc +++ b/krita/ui/dialogs/kis_dlg_image_properties.cc @@ -1,133 +1,132 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_dlg_image_properties.h" #include #include #include #include #include #include #include #include #include #include #include #include "KoColorProfile.h" #include "KoColor.h" #include "KoColorPopupAction.h" #include "kis_icon_utils.h" #include "KoID.h" -#include "kis_types.h" #include "kis_image.h" #include "kis_annotation.h" #include "kis_config.h" #include "kis_signal_compressor.h" #include "widgets/kis_cmb_idlist.h" #include "widgets/squeezedcombobox.h" KisDlgImageProperties::KisDlgImageProperties(KisImageWSP image, QWidget *parent, const char *name) : KoDialog(parent) { setButtons(Ok | Cancel); setDefaultButton(Ok); setObjectName(name); setCaption(i18n("Image Properties")); m_page = new WdgImageProperties(this); m_image = image; setMainWidget(m_page); resize(m_page->sizeHint()); KisConfig cfg; m_page->lblWidthValue->setText(QString::number(image->width())); m_page->lblHeightValue->setText(QString::number(image->height())); m_page->lblResolutionValue->setText(QLocale().toString(image->xRes()*72, 2)); // XXX: separate values for x & y? m_defaultColorAction = new KoColorPopupAction(this); m_defaultColorAction->setCurrentColor(m_image->defaultProjectionColor()); m_defaultColorAction->setIcon(KisIconUtils::loadIcon("format-stroke-color")); m_defaultColorAction->setToolTip(i18n("Change the background color of the image")); m_page->bnBackgroundColor->setDefaultAction(m_defaultColorAction); KisSignalCompressor *compressor = new KisSignalCompressor(500 /* ms */, KisSignalCompressor::POSTPONE, this); connect(m_defaultColorAction, SIGNAL(colorChanged(const KoColor&)), compressor, SLOT(start())); connect(compressor, SIGNAL(timeout()), this, SLOT(setCurrentColor())); connect(m_defaultColorAction, SIGNAL(colorChanged(const KoColor&)), this, SLOT(setCurrentColor())); m_page->colorSpaceSelector->setCurrentColorSpace(image->colorSpace()); vKisAnnotationSP_it beginIt = image->beginAnnotations(); vKisAnnotationSP_it endIt = image->endAnnotations(); vKisAnnotationSP_it it = beginIt; while (it != endIt) { if (!(*it) || (*it)->type().isEmpty()) { dbgFile << "Warning: empty annotation"; it++; continue; } m_page->cmbAnnotations->addItem((*it) -> type()); it++; } connect(m_page->cmbAnnotations, SIGNAL(activated(QString)), SLOT(setAnnotation(QString))); setAnnotation(m_page->cmbAnnotations->currentText()); } KisDlgImageProperties::~KisDlgImageProperties() { delete m_page; } const KoColorSpace * KisDlgImageProperties::colorSpace() { return m_page->colorSpaceSelector->currentColorSpace(); } void KisDlgImageProperties::setCurrentColor() { m_image->setDefaultProjectionColor(m_defaultColorAction->currentKoColor()); m_image->refreshGraphAsync(); } void KisDlgImageProperties::setAnnotation(const QString &type) { KisAnnotationSP annotation = m_image->annotation(type); if (annotation) { m_page->lblDescription->clear(); m_page->txtAnnotation->clear(); m_page->lblDescription->setText(annotation->description()); m_page->txtAnnotation->appendPlainText(annotation->displayText()); } else { m_page->lblDescription->clear(); m_page->txtAnnotation->clear(); } } diff --git a/krita/ui/dialogs/kis_dlg_image_properties.h b/krita/ui/dialogs/kis_dlg_image_properties.h index a3bb3be655..eff9da1ee2 100644 --- a/krita/ui/dialogs/kis_dlg_image_properties.h +++ b/krita/ui/dialogs/kis_dlg_image_properties.h @@ -1,67 +1,66 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_DLG_IMAGE_PROPERTIES_H_ #define KIS_DLG_IMAGE_PROPERTIES_H_ #include #include #include "ui_wdgimageproperties.h" class KoColorSpace; -class KoColor; class KoColorPopupAction; class WdgImageProperties : public QWidget, public Ui::WdgImageProperties { Q_OBJECT public: WdgImageProperties(QWidget *parent) : QWidget(parent) { setupUi(this); } }; class KisDlgImageProperties : public KoDialog { Q_OBJECT public: KisDlgImageProperties(KisImageWSP image, QWidget *parent = 0, const char *name = 0); virtual ~KisDlgImageProperties(); const KoColorSpace * colorSpace(); private Q_SLOTS: void setAnnotation(const QString& type); void setCurrentColor(); private: WdgImageProperties * m_page; KisImageWSP m_image; KoColorPopupAction *m_defaultColorAction; }; #endif // KIS_DLG_IMAGE_PROPERTIES_H_ diff --git a/krita/ui/input/config/kis_input_button.cpp b/krita/ui/input/config/kis_input_button.cpp index 45330f5eb0..8c99075756 100644 --- a/krita/ui/input/config/kis_input_button.cpp +++ b/krita/ui/input/config/kis_input_button.cpp @@ -1,231 +1,230 @@ /* * This file is part of the KDE project * Copyright (C) 2013 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_input_button.h" #include #include #include #include #include #include "kis_icon_utils.h" -#include "input/kis_shortcut_configuration.h" class KisInputButton::Private { public: Private(KisInputButton *qq) : q(qq), type(KeyType), newInput(false), resetTimer(0) { } void updateLabel(); KisInputButton *q; ButtonType type; QList keys; Qt::MouseButtons buttons; KisShortcutConfiguration::MouseWheelMovement wheel; bool newInput; QTimer *resetTimer; }; KisInputButton::KisInputButton(QWidget *parent) : QPushButton(parent), d(new Private(this)) { setIcon(KisIconUtils::loadIcon("configure")); setText(i18nc("No input for this button", "None")); setCheckable(true); d->resetTimer = new QTimer(this); d->resetTimer->setInterval(5000); d->resetTimer->setSingleShot(true); connect(d->resetTimer, SIGNAL(timeout()), SLOT(reset())); } KisInputButton::~KisInputButton() { delete d; } KisInputButton::ButtonType KisInputButton::type() const { return d->type; } void KisInputButton::setType(KisInputButton::ButtonType newType) { d->type = newType; } void KisInputButton::clear() { d->keys.clear(); d->buttons = 0; d->wheel = KisShortcutConfiguration::NoMovement; d->updateLabel(); } QList< Qt::Key > KisInputButton::keys() const { return d->keys; } void KisInputButton::setKeys(const QList< Qt::Key > &newKeys) { if (newKeys != d->keys) { d->keys = newKeys; d->updateLabel(); } } Qt::MouseButtons KisInputButton::buttons() const { return d->buttons; } void KisInputButton::setButtons(Qt::MouseButtons newButtons) { if (newButtons != d->buttons) { d->buttons = newButtons; d->updateLabel(); } } KisShortcutConfiguration::MouseWheelMovement KisInputButton::wheel() const { return d->wheel; } void KisInputButton::setWheel(KisShortcutConfiguration::MouseWheelMovement newWheel) { if (newWheel != d->wheel) { d->wheel = newWheel; d->updateLabel(); } } void KisInputButton::mousePressEvent(QMouseEvent *event) { if (isChecked() && d->type == KisInputButton::MouseType) { d->buttons = event->buttons(); d->updateLabel(); d->resetTimer->start(); } } void KisInputButton::mouseReleaseEvent(QMouseEvent *) { if (isChecked()) { reset(); } else { setChecked(true); setText(i18nc("Waiting for user input", "Input...")); d->resetTimer->start(); d->newInput = true; } } void KisInputButton::wheelEvent(QWheelEvent *event) { if (isChecked() && event->delta() != 0) { switch (event->orientation()) { case Qt::Horizontal: if (event->delta() < 0) { d->wheel = KisShortcutConfiguration::WheelRight; } else { d->wheel = KisShortcutConfiguration::WheelLeft; } break; case Qt::Vertical: if (event->delta() > 0) { d->wheel = KisShortcutConfiguration::WheelUp; } else { d->wheel = KisShortcutConfiguration::WheelDown; } break; } d->updateLabel(); } } void KisInputButton::keyPressEvent(QKeyEvent *event) { if (isChecked()) { if (d->newInput) { d->keys.clear(); d->newInput = false; } Qt::Key key = static_cast(event->key()); if (key == Qt::Key_Meta && event->modifiers().testFlag(Qt::ShiftModifier)) { key = Qt::Key_Alt; } d->keys.append(key); d->updateLabel(); d->resetTimer->start(); } } void KisInputButton::keyReleaseEvent(QKeyEvent *event) { if (isChecked()) { reset(); } else if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { setChecked(true); setText(i18nc("Waiting for user input", "Input...")); d->resetTimer->start(); d->newInput = true; } } void KisInputButton::reset() { setChecked(false); d->updateLabel(); emit dataChanged(); } void KisInputButton::Private::updateLabel() { switch (type) { case MouseType: q->setText(KisShortcutConfiguration::buttonsToText(buttons)); break; case KeyType: q->setText(KisShortcutConfiguration::keysToText(keys)); break; case WheelType: q->setText(KisShortcutConfiguration::wheelToText(wheel)); break; } } diff --git a/krita/ui/input/kis_alternate_invocation_action.cpp b/krita/ui/input/kis_alternate_invocation_action.cpp index 5ea405e85a..0572c87694 100644 --- a/krita/ui/input/kis_alternate_invocation_action.cpp +++ b/krita/ui/input/kis_alternate_invocation_action.cpp @@ -1,160 +1,159 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_alternate_invocation_action.h" #include #include #include #include #include "kis_input_manager.h" -#include "kis_tool.h" #include "kis_cursor.h" struct KisAlternateInvocationAction::Private { KisTool::ToolAction savedAction; }; KisAlternateInvocationAction::KisAlternateInvocationAction() : KisAbstractInputAction("Alternate Invocation") , m_d(new Private) { setName(i18n("Alternate Invocation")); setDescription(i18n("The Alternate Invocation action performs an alternate action with the current tool. For example, using the brush tool it picks a color from the canvas.")); QHash shortcuts; shortcuts.insert(i18n("Primary Mode"), PrimaryAlternateModeShortcut); shortcuts.insert(i18n("Secondary Mode"), SecondaryAlternateModeShortcut); shortcuts.insert(i18n("Pick Foreground Color from Current Layer"), PickColorFgLayerModeShortcut); shortcuts.insert(i18n("Pick Background Color from Current Layer"), PickColorBgLayerModeShortcut); shortcuts.insert(i18n("Pick Foreground Color from Merged Image"), PickColorFgImageModeShortcut); shortcuts.insert(i18n("Pick Background Color from Merged Image"), PickColorBgImageModeShortcut); setShortcutIndexes(shortcuts); } KisAlternateInvocationAction::~KisAlternateInvocationAction() { } KisTool::ToolAction KisAlternateInvocationAction::shortcutToToolAction(int shortcut) { KisTool::ToolAction action = KisTool::Alternate_NONE; switch ((Shortcut)shortcut) { case PickColorFgLayerModeShortcut: action = KisTool::AlternatePickFgNode; break; case PickColorBgLayerModeShortcut: action = KisTool::AlternatePickBgNode; break; case PickColorFgImageModeShortcut: action = KisTool::AlternatePickFgImage; break; case PickColorBgImageModeShortcut: action = KisTool::AlternatePickBgImage; break; case PrimaryAlternateModeShortcut: action = KisTool::AlternateSecondary; break; case SecondaryAlternateModeShortcut: action = KisTool::AlternateThird; break; } return action; } void KisAlternateInvocationAction::activate(int shortcut) { KisTool::ToolAction action = shortcutToToolAction(shortcut); inputManager()->toolProxy()->activateToolAction(action); } void KisAlternateInvocationAction::deactivate(int shortcut) { KisTool::ToolAction action = shortcutToToolAction(shortcut); inputManager()->toolProxy()->deactivateToolAction(action); } int KisAlternateInvocationAction::priority() const { return 9; } void KisAlternateInvocationAction::begin(int shortcut, QEvent *event) { if (!event) return; KisAbstractInputAction::begin(shortcut, event); QMouseEvent targetEvent(QEvent::MouseButtonPress, eventPos(event), Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier); // There must be a better way m_d->savedAction = shortcutToToolAction(shortcut); inputManager()->toolProxy()->forwardEvent(KisToolProxy::BEGIN, m_d->savedAction, &targetEvent, event); } void KisAlternateInvocationAction::end(QEvent *event) { if (!event) return; Qt::KeyboardModifiers modifiers; switch (m_d->savedAction) { case KisTool::AlternatePickFgNode: modifiers = Qt::ControlModifier; break; case KisTool::AlternateThird: modifiers = Qt::ControlModifier | Qt::AltModifier; break; default: ; } QMouseEvent targetEvent = QMouseEvent(QEvent::MouseButtonRelease, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers); inputManager()->toolProxy()->forwardEvent(KisToolProxy::END, m_d->savedAction, &targetEvent, event); KisAbstractInputAction::end(event); } void KisAlternateInvocationAction::inputEvent(QEvent* event) { if (event && ((event->type() == QEvent::MouseMove) || (event->type() == QEvent::TabletMove))) { Qt::KeyboardModifiers modifiers; switch (m_d->savedAction) { case KisTool::AlternatePickFgNode: modifiers = Qt::ControlModifier; break; case KisTool::AlternateThird: modifiers = Qt::ControlModifier | Qt::AltModifier; break; default: modifiers = Qt::ShiftModifier; } QMouseEvent targetEvent(QEvent::MouseMove, eventPos(event), Qt::LeftButton, Qt::LeftButton, modifiers); inputManager()->toolProxy()->forwardEvent(KisToolProxy::CONTINUE, m_d->savedAction, &targetEvent, event); } } diff --git a/krita/ui/input/kis_input_manager.h b/krita/ui/input/kis_input_manager.h index 4df880bb48..dc4439bab1 100644 --- a/krita/ui/input/kis_input_manager.h +++ b/krita/ui/input/kis_input_manager.h @@ -1,125 +1,124 @@ /* This file is part of the KDE project * Copyright (C) 2012 Arjen Hiemstra * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_INPUTMANAGER_H #define KIS_INPUTMANAGER_H #include #include class QPointF; -class QTabletEvent; class QTouchEvent; class KisToolProxy; class KisCanvas2; /** * \brief Central object to manage canvas input. * * The Input Manager class manages all canvas input. It is created * by KisCanvas2 and processes all events related to input sent to the * canvas. * * The Input Manager keeps track of a set of actions and a set of * shortcuts. The actions are pre-defined while the shortcuts are * set from configuration. * * For each event, it will try to determine if there is a shortcut that * matches the input. It will then activate this action and pass all * consecutive events on to this action. * * \sa KisAbstractInputAction * * \todo Implement shortcut configuration */ class KRITAUI_EXPORT KisInputManager : public QObject { Q_OBJECT public: /** * Constructor. */ KisInputManager(QObject *parent); /** * Destructor. */ ~KisInputManager(); void addTrackedCanvas(KisCanvas2 *canvas); void removeTrackedCanvas(KisCanvas2 *canvas); void toggleTabletLogger(); /** * Installs the input manager as an event filter for \p receiver. * Please note that KisInputManager is supposed to handle events * for a single receiver only. This is defined by the fact that it * resends some of the events back through the Qt's queue to the * reciever. That is why the input manager will assert when it gets * an event with wrong destination. */ void setupAsEventFilter(QObject *receiver); /** * Event filter method. Overridden from QObject. */ bool eventFilter(QObject* object, QEvent* event ); void attachPriorityEventFilter(QObject *filter); void detachPriorityEventFilter(QObject *filter); /** * Return the canvas this input manager is associated with. */ KisCanvas2 *canvas() const; /** * The tool proxy of the current application. */ KisToolProxy *toolProxy() const; /** * Touch events are special, too. * * \return a touch event if there was one, otherwise 0 */ QTouchEvent *lastTouchEvent() const; /** * Convert a widget position to a document position. */ QPointF widgetToDocument(const QPointF &position); public Q_SLOTS: void stopIgnoringEvents(); void slotFocusOnEnter(bool value); private Q_SLOTS: void slotToolChanged(); void profileChanged(); void slotCompressedMoveEvent(); private: class Private; Private* const d; }; #endif // KIS_INPUTMANAGER_H diff --git a/krita/ui/input/kis_input_manager_p.cpp b/krita/ui/input/kis_input_manager_p.cpp index 81abb86643..f563f799f6 100644 --- a/krita/ui/input/kis_input_manager_p.cpp +++ b/krita/ui/input/kis_input_manager_p.cpp @@ -1,425 +1,426 @@ /* * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_input_manager_p.h" #include #include #include #include +#include "kis_input_manager.h" #include "kis_config.h" #include "kis_abstract_input_action.h" +#include "kis_tool_invocation_action.h" #include "kis_stroke_shortcut.h" #include "kis_touch_shortcut.h" #include "kis_input_profile_manager.h" -#include "input/kis_tablet_debugger.h" // Note: this class is intended to model all event masking logic. class EventEater : public QObject { public: EventEater() : QObject(0), hungry(false) {} bool eventFilter(QObject* /*object*/, QEvent* event ) { if ((hungry && (event->type() == QEvent::MouseMove || event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease)) || (peckish && (event->type() == QEvent::MouseButtonPress))) { if (KisTabletDebugger::instance()->debugEnabled()) { QString pre = QString("[BLOCKED]"); QMouseEvent *ev = static_cast(event); dbgInput << KisTabletDebugger::instance()->eventToString(*ev,pre); } peckish = false; return true; } return false; } void activate() { if (!hungry && (KisTabletDebugger::instance()->debugEnabled())) dbgInput << "Ignoring mouse events."; hungry = true; } void deactivate() { if (!hungry && (KisTabletDebugger::instance()->debugEnabled())) dbgInput << "Accepting mouse events."; hungry = false; } void eatOneMousePress() { peckish = true; } bool isActive() { return hungry; } private: bool hungry; // Continue eating mouse strokes bool peckish; // Eat a single mouse press event }; Q_GLOBAL_STATIC(EventEater, globalEventEater); bool KisInputManager::Private::ignoreQtCursorEvents() { return globalEventEater->isActive(); } KisInputManager::Private::Private(KisInputManager *qq) : q(qq) , canvas(0) , toolProxy(0) , forwardAllEventsToTool(false) , disableTouchOnCanvas(false) , touchHasBlockedPressEvents(false) , lastTouchEvent(0) , defaultInputAction(0) , eventsReceiver(0) , moveEventCompressor(10 /* ms */, KisSignalCompressor::FIRST_ACTIVE) , testingAcceptCompressedTabletEvents(false) , testingCompressBrushEvents(false) , focusOnEnter(true) , containsPointer(true) { KisConfig cfg; disableTouchOnCanvas = cfg.disableTouchOnCanvas(); moveEventCompressor.setDelay(cfg.tabletEventsDelay()); testingAcceptCompressedTabletEvents = cfg.testingAcceptCompressedTabletEvents(); testingCompressBrushEvents = cfg.testingCompressBrushEvents(); setupActions(); canvasSwitcher = new CanvasSwitcher(this, q); qApp->installEventFilter(globalEventEater); } KisInputManager::Private::~Private() { delete canvasSwitcher; } KisInputManager::Private::CanvasSwitcher::CanvasSwitcher(Private *_d, QObject *p) : QObject(p), d(_d), eatOneMouseStroke(false) { } void KisInputManager::Private::CanvasSwitcher::addCanvas(KisCanvas2 *canvas) { QObject *widget = canvas->canvasWidget(); if (!canvasResolver.contains(widget)) { canvasResolver.insert(widget, canvas); d->q->setupAsEventFilter(widget); widget->installEventFilter(this); d->canvas = canvas; d->toolProxy = dynamic_cast(canvas->toolProxy()); } else { KIS_ASSERT_RECOVER_RETURN(d->canvas == canvas); } } void KisInputManager::Private::CanvasSwitcher::removeCanvas(KisCanvas2 *canvas) { QObject *widget = canvas->canvasWidget(); canvasResolver.remove(widget); if (d->eventsReceiver == widget) { d->q->setupAsEventFilter(0); } widget->removeEventFilter(this); } bool KisInputManager::Private::CanvasSwitcher::eventFilter(QObject* object, QEvent* event ) { if (canvasResolver.contains(object)) { switch (event->type()) { case QEvent::FocusIn: { QFocusEvent *fevent = static_cast(event); eatOneMouseStroke = 2 * (fevent->reason() == Qt::MouseFocusReason); KisCanvas2 *canvas = canvasResolver.value(object); d->canvas = canvas; d->toolProxy = dynamic_cast(canvas->toolProxy()); d->q->setupAsEventFilter(object); object->removeEventFilter(this); object->installEventFilter(this); QEvent event(QEvent::Enter); d->q->eventFilter(object, &event); break; } case QEvent::Wheel: { QWidget *widget = static_cast(object); widget->setFocus(); break; } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::TabletPress: case QEvent::TabletRelease: if (eatOneMouseStroke) { eatOneMouseStroke--; return true; } break; case QEvent::MouseButtonDblClick: if (eatOneMouseStroke) { return true; } break; default: break; } } return QObject::eventFilter(object, event); } KisInputManager::Private::ProximityNotifier::ProximityNotifier(KisInputManager::Private *_d, QObject *p) : QObject(p), d(_d) {} bool KisInputManager::Private::ProximityNotifier::eventFilter(QObject* object, QEvent* event ) { switch (event->type()) { case QEvent::TabletEnterProximity: d->debugEvent(event); globalEventEater->eatOneMousePress(); // d->blockMouseEvents(); Qt sends fake mouse events instead of hover events, so disable this for now. break; case QEvent::TabletLeaveProximity: d->debugEvent(event); d->allowMouseEvents(); break; default: break; } return QObject::eventFilter(object, event); } void KisInputManager::Private::addStrokeShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, Qt::MouseButtons buttons) { KisStrokeShortcut *strokeShortcut = new KisStrokeShortcut(action, index); QList buttonList; if(buttons & Qt::LeftButton) { buttonList << Qt::LeftButton; } if(buttons & Qt::RightButton) { buttonList << Qt::RightButton; } if(buttons & Qt::MidButton) { buttonList << Qt::MidButton; } if(buttons & Qt::XButton1) { buttonList << Qt::XButton1; } if(buttons & Qt::XButton2) { buttonList << Qt::XButton2; } if (buttonList.size() > 0) { strokeShortcut->setButtons(QSet::fromList(modifiers), QSet::fromList(buttonList)); matcher.addShortcut(strokeShortcut); } } void KisInputManager::Private::addKeyShortcut(KisAbstractInputAction* action, int index, const QList &keys) { if (keys.size() == 0) return; KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); //Note: Ordering is important here, Shift + V is different from V + Shift, //which is the reason we use the last key here since most users will enter //shortcuts as "Shift + V". Ideally this should not happen, but this is //the way the shortcut matcher is currently implemented. QList allKeys = keys; Qt::Key key = allKeys.takeLast(); QSet modifiers = QSet::fromList(allKeys); keyShortcut->setKey(modifiers, key); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addWheelShortcut(KisAbstractInputAction* action, int index, const QList &modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction) { KisSingleActionShortcut *keyShortcut = new KisSingleActionShortcut(action, index); KisSingleActionShortcut::WheelAction a; switch(wheelAction) { case KisShortcutConfiguration::WheelUp: a = KisSingleActionShortcut::WheelUp; break; case KisShortcutConfiguration::WheelDown: a = KisSingleActionShortcut::WheelDown; break; case KisShortcutConfiguration::WheelLeft: a = KisSingleActionShortcut::WheelLeft; break; case KisShortcutConfiguration::WheelRight: a = KisSingleActionShortcut::WheelRight; break; default: return; } keyShortcut->setWheel(QSet::fromList(modifiers), a); matcher.addShortcut(keyShortcut); } void KisInputManager::Private::addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture) { KisTouchShortcut *shortcut = new KisTouchShortcut(action, index); switch(gesture) { case KisShortcutConfiguration::PinchGesture: shortcut->setMinimumTouchPoints(2); shortcut->setMaximumTouchPoints(2); break; case KisShortcutConfiguration::PanGesture: shortcut->setMinimumTouchPoints(3); shortcut->setMaximumTouchPoints(10); break; default: break; } matcher.addShortcut(shortcut); } void KisInputManager::Private::setupActions() { QList actions = KisInputProfileManager::instance()->actions(); foreach(KisAbstractInputAction *action, actions) { KisToolInvocationAction *toolAction = dynamic_cast(action); if(toolAction) { defaultInputAction = toolAction; } } connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), q, SLOT(profileChanged())); if(KisInputProfileManager::instance()->currentProfile()) { q->profileChanged(); } } bool KisInputManager::Private::processUnhandledEvent(QEvent *event) { bool retval = false; if (forwardAllEventsToTool || event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { defaultInputAction->processUnhandledEvent(event); retval = true; } return retval && !forwardAllEventsToTool; } Qt::Key KisInputManager::Private::workaroundShiftAltMetaHell(const QKeyEvent *keyEvent) { Qt::Key key = (Qt::Key)keyEvent->key(); if (keyEvent->key() == Qt::Key_Meta && keyEvent->modifiers().testFlag(Qt::ShiftModifier)) { key = Qt::Key_Alt; } return key; } bool KisInputManager::Private::tryHidePopupPalette() { if (canvas->isPopupPaletteVisible()) { canvas->slotShowPopupPalette(); return true; } return false; } #ifdef HAVE_X11 inline QPointF dividePoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() / pt2.x(), pt1.y() / pt2.y()); } inline QPointF multiplyPoints(const QPointF &pt1, const QPointF &pt2) { return QPointF(pt1.x() * pt2.x(), pt1.y() * pt2.y()); } #endif void KisInputManager::Private::saveTouchEvent( QTouchEvent* event ) { delete lastTouchEvent; lastTouchEvent = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); } void KisInputManager::Private::blockMouseEvents() { globalEventEater->activate(); } void KisInputManager::Private::allowMouseEvents() { globalEventEater->deactivate(); } bool KisInputManager::Private::handleCompressedTabletEvent(QObject *object, QTabletEvent *tevent) { if(object == 0) return false; bool retval = false; retval = q->eventFilter(object, tevent); if (!retval && !tevent->isAccepted()) { dbgInput << "Rejected a compressed tablet event."; } return retval; } diff --git a/krita/ui/input/kis_input_manager_p.h b/krita/ui/input/kis_input_manager_p.h index 5daa55df4d..5d3dec0915 100644 --- a/krita/ui/input/kis_input_manager_p.h +++ b/krita/ui/input/kis_input_manager_p.h @@ -1,119 +1,117 @@ /* * Copyright (C) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "kis_input_manager.h" #include "kis_shortcut_matcher.h" -#include "kis_tool_invocation_action.h" -#include "kis_alternate_invocation_action.h" #include "kis_shortcut_configuration.h" #include "kis_canvas2.h" #include "kis_tool_proxy.h" #include "kis_signal_compressor.h" #include "input/kis_tablet_debugger.h" -#include "kis_abstract_input_action.h" +class KisToolInvocationAction; class KisInputManager::Private { public: Private(KisInputManager *qq); ~Private(); bool tryHidePopupPalette(); void addStrokeShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, Qt::MouseButtons buttons); void addKeyShortcut(KisAbstractInputAction* action, int index,const QList &keys); void addTouchShortcut( KisAbstractInputAction* action, int index, KisShortcutConfiguration::GestureAction gesture ); void addWheelShortcut(KisAbstractInputAction* action, int index, const QList< Qt::Key >& modifiers, KisShortcutConfiguration::MouseWheelMovement wheelAction); bool processUnhandledEvent(QEvent *event); Qt::Key workaroundShiftAltMetaHell(const QKeyEvent *keyEvent); void setupActions(); void saveTouchEvent( QTouchEvent* event ); bool handleCompressedTabletEvent(QObject *object, QTabletEvent *tevent); KisInputManager *q; KisCanvas2 *canvas; KisToolProxy *toolProxy; bool forwardAllEventsToTool; bool ignoreQtCursorEvents(); bool disableTouchOnCanvas; bool touchHasBlockedPressEvents; KisShortcutMatcher matcher; QTouchEvent *lastTouchEvent; KisToolInvocationAction *defaultInputAction; QObject *eventsReceiver; KisSignalCompressor moveEventCompressor; QScopedPointer compressedMoveEvent; bool testingAcceptCompressedTabletEvents; bool testingCompressBrushEvents; QSet > priorityEventFilter; void blockMouseEvents(); void allowMouseEvents(); template void debugEvent(QEvent *event) { if (!KisTabletDebugger::instance()->debugEnabled()) return; QString msg1 = useBlocking && ignoreQtCursorEvents() ? "[BLOCKED] " : "[ ]"; Event *specificEvent = static_cast(event); dbgInput << KisTabletDebugger::instance()->eventToString(*specificEvent, msg1); } class ProximityNotifier : public QObject { public: ProximityNotifier(Private *_d, QObject *p); bool eventFilter(QObject* object, QEvent* event ); private: KisInputManager::Private *d; }; class CanvasSwitcher : public QObject { public: CanvasSwitcher(Private *_d, QObject *p); void addCanvas(KisCanvas2 *canvas); void removeCanvas(KisCanvas2 *canvas); bool eventFilter(QObject* object, QEvent* event ); private: KisInputManager::Private *d; QMap canvasResolver; int eatOneMouseStroke; }; QPointer canvasSwitcher; bool focusOnEnter; bool containsPointer; }; diff --git a/krita/ui/input/kis_shortcut_matcher.h b/krita/ui/input/kis_shortcut_matcher.h index b73aefceea..1bd0b4931d 100644 --- a/krita/ui/input/kis_shortcut_matcher.h +++ b/krita/ui/input/kis_shortcut_matcher.h @@ -1,231 +1,228 @@ /* * Copyright (c) 2012 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_SHORTCUT_MATCHER_H #define __KIS_SHORTCUT_MATCHER_H -#include "kis_abstract_shortcut.h" #include #include "kis_single_action_shortcut.h" class QEvent; -class QMouseEvent; -class QKeyEvent; class QWheelEvent; class QTouchEvent; class QString; class KisStrokeShortcut; class KisTouchShortcut; /** * The class that manages connections between shortcuts and actions. * * It processes input events and generates state transitions for the * actions basing on the data, represented by the shortcuts. * * The class works with two types of actions: long running * (represented by KisStrokeShortcuts) and "atomic" * (KisSingleActionShortcut). The former one invole some long * interaction with the user by means of a mouse cursor or a tablet, * the latter one simple action like "Zoom 100%" or "Reset Rotation". * * The single action shortcuts are handled quite easily. The matcher * listens to the events coming, manages two lists of the pressed keys * and buttons and when their content corresponds to some single * action shortcut it just runs this shortcut once. * * The strategy for handling the stroke shortcuts is a bit more * complex. Each such action may be in one of the three states: * * Idle <-> Ready <-> Running * * In "Idle" state the action is completely inactive and has no access * to the user * * When the action is in "Ready" state, it means that all the * modifiers for the action are already pressed and we are only * waiting for a user to press the mouse button and start a stroke. In * this state the action can show the user its Cursor to notify the user * what is going to happen next. * * In the "Running" state, the action has full access to the user * input and is considered to perform all the work it was created for. * * To implement such state transitions for the actions, * KisShortcutMatcher first forms a list of the actions which can be * moved to a ready state (m_d->readyShortcuts), then chooses the one * with the highest priority to be the only shortcut in the "Ready" * state and activates it (m_d->readyShortcut). Then when the user * presses the mouse button, the matcher looks through the list of * ready shortcuts, chooses which will be running now, deactivates (if * needed) currently activated action and starts the chosen one. * * \see KisSingleActionShortcut * \see KisStrokeShortcut */ class KRITAUI_EXPORT KisShortcutMatcher { public: KisShortcutMatcher(); ~KisShortcutMatcher(); void addShortcut(KisSingleActionShortcut *shortcut); void addShortcut(KisStrokeShortcut *shortcut); void addShortcut(KisTouchShortcut *shortcut); /** * Returns true if the currently running shortcut supports * processing hi resolution flow of events from the tablet * device. In most of the cases (except of the painting itself) * too many events make the execution of the action too slow, so * the action can decide whether it needs it. */ bool supportsHiResInputEvents(); /** * Handles a key press event. * No autorepeat events should be passed to this method. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool keyPressed(Qt::Key key); /** * Handles a key press event that has been generated by the * autorepeat. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool autoRepeatedKeyPressed(Qt::Key key); /** * Handles a key release event. * No autorepeat events should be passed to this method. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool keyReleased(Qt::Key key); /** * Handles button presses from a tablet or mouse. * * \param event the event that caused this call. * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool buttonPressed(Qt::MouseButton button, QEvent *event); /** * Handles the mouse button release event * * \param event the event that caused this call. * Must be of type QTabletEvent or QMouseEvent. * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool buttonReleased(Qt::MouseButton button, QEvent *event); /** * Handles the mouse wheel event * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool wheelEvent(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event); /** * Handles tablet and mouse move events. * * \param event the event that caused this call * * \return whether the event has been handled successfully and * should be eaten by the events filter */ bool pointerMoved(QEvent *event); /** * Handle cursor's Enter event. * We never eat it because it might be used by someone else */ void enterEvent(); /** * Handle cursor's Leave event. * We never eat it because it might be used by someone else */ void leaveEvent(); bool touchBeginEvent(QTouchEvent *event); bool touchUpdateEvent(QTouchEvent *event); bool touchEndEvent(QTouchEvent *event); /** * Resets the internal state of the matcher and activates the * prepared action if possible. * * This should be done when the window has lost the focus for * some time, so that several events could be lost */ void reinitialize(); /** * Disables the start of any actions. * * WARNING: the actions that has been started before this call * will *not* be ended. They will be ended in their usual way, * when the mouse button will be released. */ void suppressAllActions(bool value); /** * Remove all shortcuts that have been registered. */ void clearShortcuts(); private: friend class KisInputManagerTest; void reset(); void reset(QString msg); bool tryRunWheelShortcut(KisSingleActionShortcut::WheelAction wheelAction, QWheelEvent *event); template bool tryRunSingleActionShortcutImpl(T param, U *event, const QSet &keysState); void prepareReadyShortcuts(); bool tryRunReadyShortcut( Qt::MouseButton button, QEvent* event ); void tryActivateReadyShortcut(); bool tryEndRunningShortcut( Qt::MouseButton button, QEvent* event ); bool tryRunTouchShortcut(QTouchEvent *event); bool tryEndTouchShortcut(QTouchEvent *event); private: class Private; Private * const m_d; }; #endif /* __KIS_SHORTCUT_MATCHER_H */ diff --git a/krita/ui/kis_asl_layer_style_serializer.cpp b/krita/ui/kis_asl_layer_style_serializer.cpp index d91c6ad6f8..e2fda112f4 100644 --- a/krita/ui/kis_asl_layer_style_serializer.cpp +++ b/krita/ui/kis_asl_layer_style_serializer.cpp @@ -1,1238 +1,1236 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_asl_layer_style_serializer.h" #include #include #include #include #include #include #include #include #include "kis_dom_utils.h" #include "psd.h" #include "kis_global.h" -#include "kis_psd_layer_style.h" #include "asl/kis_asl_reader.h" #include "asl/kis_asl_xml_parser.h" -#include "asl/kis_asl_callback_object_catcher.h" #include "asl/kis_asl_writer_utils.h" #include "asl/kis_asl_xml_writer.h" #include "asl/kis_asl_writer.h" KisAslLayerStyleSerializer::KisAslLayerStyleSerializer() { } KisAslLayerStyleSerializer::~KisAslLayerStyleSerializer() { } QVector KisAslLayerStyleSerializer::styles() const { return m_stylesVector; } void KisAslLayerStyleSerializer::setStyles(const QVector &styles) { m_stylesVector = styles; } QString compositeOpToBlendMode(const QString &compositeOp) { QString mode = "Nrml"; if (compositeOp == COMPOSITE_OVER) { mode = "Nrml"; } else if (compositeOp == COMPOSITE_DISSOLVE) { mode = "Dslv"; } else if (compositeOp == COMPOSITE_DARKEN) { mode = "Drkn"; } else if (compositeOp == COMPOSITE_MULT) { mode = "Mltp"; } else if (compositeOp == COMPOSITE_BURN) { mode = "CBrn"; } else if (compositeOp == COMPOSITE_LINEAR_BURN) { mode = "linearBurn"; } else if (compositeOp == COMPOSITE_DARKER_COLOR) { mode = "darkerColor"; } else if (compositeOp == COMPOSITE_LIGHTEN) { mode = "Lghn"; } else if (compositeOp == COMPOSITE_SCREEN) { mode = "Scrn"; } else if (compositeOp == COMPOSITE_DODGE) { mode = "CDdg"; } else if (compositeOp == COMPOSITE_LINEAR_DODGE) { mode = "linearDodge"; } else if (compositeOp == COMPOSITE_LIGHTER_COLOR) { mode = "lighterColor"; } else if (compositeOp == COMPOSITE_OVERLAY) { mode = "Ovrl"; } else if (compositeOp == COMPOSITE_SOFT_LIGHT_PHOTOSHOP) { mode = "SftL"; } else if (compositeOp == COMPOSITE_HARD_LIGHT) { mode = "HrdL"; } else if (compositeOp == COMPOSITE_VIVID_LIGHT) { mode = "vividLight"; } else if (compositeOp == COMPOSITE_LINEAR_LIGHT) { mode = "linearLight"; } else if (compositeOp == COMPOSITE_PIN_LIGHT) { mode = "pinLight"; } else if (compositeOp == COMPOSITE_HARD_MIX) { mode = "hardMix"; } else if (compositeOp == COMPOSITE_DIFF) { mode = "Dfrn"; } else if (compositeOp == COMPOSITE_EXCLUSION) { mode = "Xclu"; } else if (compositeOp == COMPOSITE_SUBTRACT) { mode = "Sbtr"; } else if (compositeOp == COMPOSITE_DIVIDE) { mode = "divide"; } else if (compositeOp == COMPOSITE_HUE) { mode = "H "; } else if (compositeOp == COMPOSITE_SATURATION) { mode = "Strt"; } else if (compositeOp == COMPOSITE_COLOR) { mode = "Clr "; } else if (compositeOp == COMPOSITE_LUMINIZE) { mode = "Lmns"; } else { dbgKrita << "Unknown composite op:" << mode << "Returning \"Nrml\"!"; } return mode; } QString techniqueToString(psd_technique_type technique, const QString &typeId) { QString result = "SfBL"; switch (technique) { case psd_technique_softer: result = "SfBL"; break; case psd_technique_precise: result = "PrBL"; break; case psd_technique_slope_limit: result = "Slmt"; break; } if (typeId == "BETE" && technique == psd_technique_slope_limit) { warnKrita << "WARNING: techniqueToString: invalid technique type!" << ppVar(technique) << ppVar(typeId); } return result; } QString bevelStyleToString(psd_bevel_style style) { QString result = "OtrB"; switch (style) { case psd_bevel_outer_bevel: result = "OtrB"; break; case psd_bevel_inner_bevel: result = "InrB"; break; case psd_bevel_emboss: result = "Embs"; break; case psd_bevel_pillow_emboss: result = "PlEb"; break; case psd_bevel_stroke_emboss: result = "strokeEmboss"; break; } return result; } QString gradientTypeToString(psd_gradient_style style) { QString result = "Lnr "; switch (style) { case psd_gradient_style_linear: result = "Lnr "; break; case psd_gradient_style_radial: result = "Rdl "; break; case psd_gradient_style_angle: result = "Angl"; break; case psd_gradient_style_reflected: result = "Rflc"; break; case psd_gradient_style_diamond: result = "Dmnd"; break; } return result; } QString strokePositionToString(psd_stroke_position position) { QString result = "OutF"; switch (position) { case psd_stroke_outside: result = "OutF"; break; case psd_stroke_inside: result = "InsF"; break; case psd_stroke_center: result = "CtrF"; break; } return result; } QString strokeFillTypeToString(psd_fill_type position) { QString result = "SClr"; switch (position) { case psd_fill_solid_color: result = "SClr"; break; case psd_fill_gradient: result = "GrFl"; break; case psd_fill_pattern: result = "Ptrn"; break; } return result; } QVector KisAslLayerStyleSerializer::fetchAllPatterns(KisPSDLayerStyle *style) const { QVector allPatterns; if (style->patternOverlay()->effectEnabled()) { allPatterns << style->patternOverlay()->pattern(); } if (style->stroke()->effectEnabled() && style->stroke()->fillType() == psd_fill_pattern) { allPatterns << style->stroke()->pattern(); } if(style->bevelAndEmboss()->effectEnabled() && style->bevelAndEmboss()->textureEnabled()) { allPatterns << style->bevelAndEmboss()->texturePattern(); } return allPatterns; } QString fetchPatternUuidSafe(KoPattern *pattern, QHash patternToUuid) { if (patternToUuid.contains(pattern)) { return patternToUuid[pattern]; } else { warnKrita << "WARNING: the pattern is not present in the Uuid map!"; return "invalid-uuid"; } } QDomDocument KisAslLayerStyleSerializer::formXmlDocument() const { KIS_ASSERT_RECOVER(!m_stylesVector.isEmpty()) { return QDomDocument(); } QVector allPatterns; foreach (KisPSDLayerStyleSP style, m_stylesVector) { allPatterns += fetchAllPatterns(style.data()); } QHash patternToUuidMap; KisAslXmlWriter w; if (!allPatterns.isEmpty()) { w.enterList("Patterns"); foreach (KoPattern *pattern, allPatterns) { if (pattern) { if (!patternToUuidMap.contains(pattern)) { QString uuid = w.writePattern("", pattern); patternToUuidMap.insert(pattern, uuid); } } else { warnKrita << "WARNING: KisAslLayerStyleSerializer::saveToDevice: saved pattern is null!"; } } w.leaveList(); } foreach (KisPSDLayerStyleSP style, m_stylesVector) { w.enterDescriptor("", "", "null"); w.writeText("Nm ", style->name()); w.writeText("Idnt", style->psdUuid()); w.leaveDescriptor(); w.enterDescriptor("", "", "Styl"); w.enterDescriptor("documentMode", "", "documentMode"); w.leaveDescriptor(); w.enterDescriptor("Lefx", "", "Lefx"); w.writeUnitFloat("Scl ", "#Prc", 100); w.writeBoolean("masterFXSwitch", style->isEnabled()); // Drop Shadow const psd_layer_effects_drop_shadow *dropShadow = style->dropShadow(); if (dropShadow->effectEnabled()) { w.enterDescriptor("DrSh", "", "DrSh"); w.writeBoolean("enab", dropShadow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(dropShadow->blendMode())); w.writeColor("Clr ", dropShadow->color()); w.writeUnitFloat("Opct", "#Prc", dropShadow->opacity()); w.writeBoolean("uglg", dropShadow->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", dropShadow->angle()); w.writeUnitFloat("Dstn", "#Pxl", dropShadow->distance()); w.writeUnitFloat("Ckmt", "#Pxl", dropShadow->spread()); w.writeUnitFloat("blur", "#Pxl", dropShadow->size()); w.writeUnitFloat("Nose", "#Prc", dropShadow->noise()); w.writeBoolean("AntA", dropShadow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("layerConceals", dropShadow->knocksOut()); w.leaveDescriptor(); } // Inner Shadow const psd_layer_effects_inner_shadow *innerShadow = style->innerShadow(); if (innerShadow->effectEnabled()) { w.enterDescriptor("IrSh", "", "IrSh"); w.writeBoolean("enab", innerShadow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerShadow->blendMode())); w.writeColor("Clr ", innerShadow->color()); w.writeUnitFloat("Opct", "#Prc", innerShadow->opacity()); w.writeBoolean("uglg", innerShadow->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", innerShadow->angle()); w.writeUnitFloat("Dstn", "#Pxl", innerShadow->distance()); w.writeUnitFloat("Ckmt", "#Pxl", innerShadow->spread()); w.writeUnitFloat("blur", "#Pxl", innerShadow->size()); w.writeUnitFloat("Nose", "#Prc", innerShadow->noise()); w.writeBoolean("AntA", innerShadow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.leaveDescriptor(); } // Outer Glow const psd_layer_effects_outer_glow *outerGlow = style->outerGlow(); if (outerGlow->effectEnabled()) { w.enterDescriptor("OrGl", "", "OrGl"); w.writeBoolean("enab", outerGlow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(outerGlow->blendMode())); if (outerGlow->fillType() == psd_fill_gradient && outerGlow->gradient()) { KoSegmentGradient *segmentGradient = dynamic_cast(outerGlow->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(outerGlow->gradient().data()); if (segmentGradient) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: OG: Unknown gradient type!"; w.writeColor("Clr ", outerGlow->color()); } } else { w.writeColor("Clr ", outerGlow->color()); } w.writeUnitFloat("Opct", "#Prc", outerGlow->opacity()); w.writeEnum("GlwT", "BETE", techniqueToString(outerGlow->technique(), "BETE")); w.writeUnitFloat("Ckmt", "#Pxl", outerGlow->spread()); w.writeUnitFloat("blur", "#Pxl", outerGlow->size()); w.writeUnitFloat("Nose", "#Prc", outerGlow->noise()); w.writeUnitFloat("ShdN", "#Prc", outerGlow->jitter()); w.writeBoolean("AntA", outerGlow->antiAliased()); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeUnitFloat("Inpr", "#Prc", outerGlow->range()); w.leaveDescriptor(); } // Inner Glow const psd_layer_effects_inner_glow *innerGlow = style->innerGlow(); if (innerGlow->effectEnabled()) { w.enterDescriptor("IrGl", "", "IrGl"); w.writeBoolean("enab", innerGlow->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(innerGlow->blendMode())); if (innerGlow->fillType() == psd_fill_gradient && innerGlow->gradient()) { KoSegmentGradient *segmentGradient = dynamic_cast(innerGlow->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(innerGlow->gradient().data()); if (segmentGradient) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: IG: Unknown gradient type!"; w.writeColor("Clr ", innerGlow->color()); } } else { w.writeColor("Clr ", innerGlow->color()); } w.writeUnitFloat("Opct", "#Prc", innerGlow->opacity()); w.writeEnum("GlwT", "BETE", techniqueToString(innerGlow->technique(), "BETE")); w.writeUnitFloat("Ckmt", "#Pxl", innerGlow->spread()); w.writeUnitFloat("blur", "#Pxl", innerGlow->size()); // NOTE: order is swapped in ASL! w.writeUnitFloat("ShdN", "#Prc", innerGlow->jitter()); w.writeUnitFloat("Nose", "#Prc", innerGlow->noise()); w.writeBoolean("AntA", innerGlow->antiAliased()); w.writeEnum("glwS", "IGSr", innerGlow->source() == psd_glow_center ? "SrcC" : "SrcE"); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeUnitFloat("Inpr", "#Prc", innerGlow->range()); w.leaveDescriptor(); } // Bevel and Emboss const psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss(); if (bevelAndEmboss->effectEnabled()) { w.enterDescriptor("ebbl", "", "ebbl"); w.writeBoolean("enab", bevelAndEmboss->effectEnabled()); w.writeEnum("hglM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->highlightBlendMode())); w.writeColor("hglC", bevelAndEmboss->highlightColor()); w.writeUnitFloat("hglO", "#Prc", bevelAndEmboss->highlightOpacity()); w.writeEnum("sdwM", "BlnM", compositeOpToBlendMode(bevelAndEmboss->shadowBlendMode())); w.writeColor("sdwC", bevelAndEmboss->shadowColor()); w.writeUnitFloat("sdwO", "#Prc", bevelAndEmboss->shadowOpacity()); w.writeEnum("bvlT", "bvlT", techniqueToString(bevelAndEmboss->technique(), "bvlT")); w.writeEnum("bvlS", "BESl", bevelStyleToString(bevelAndEmboss->style())); w.writeBoolean("uglg", bevelAndEmboss->useGlobalLight()); w.writeUnitFloat("lagl", "#Ang", bevelAndEmboss->angle()); w.writeUnitFloat("Lald", "#Ang", bevelAndEmboss->altitude()); w.writeUnitFloat("srgR", "#Prc", bevelAndEmboss->depth()); w.writeUnitFloat("blur", "#Pxl", bevelAndEmboss->size()); w.writeEnum("bvlD", "BESs", bevelAndEmboss->direction() == psd_direction_up ? "In " : "Out "); // FIXME: save curves w.writeCurve("TrnS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("antialiasGloss", bevelAndEmboss->glossAntiAliased()); w.writeUnitFloat("Sftn", "#Pxl", bevelAndEmboss->soften()); if (bevelAndEmboss->contourEnabled()) { w.writeBoolean("useShape", bevelAndEmboss->contourEnabled()); // FIXME: save curves w.writeCurve("MpgS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.writeBoolean("AntA", bevelAndEmboss->antiAliased()); w.writeUnitFloat("Inpr", "#Prc", bevelAndEmboss->contourRange()); } w.writeBoolean("useTexture", bevelAndEmboss->textureEnabled()); if (bevelAndEmboss->textureEnabled()) { w.writeBoolean("InvT", bevelAndEmboss->textureInvert()); w.writeBoolean("Algn", bevelAndEmboss->textureAlignWithLayer()); w.writeUnitFloat("Scl ", "#Prc", bevelAndEmboss->textureScale()); w.writeUnitFloat("textureDepth ", "#Prc", bevelAndEmboss->textureDepth()); w.writePatternRef("Ptrn", bevelAndEmboss->texturePattern(), fetchPatternUuidSafe(bevelAndEmboss->texturePattern(), patternToUuidMap)); w.writePhasePoint("phase", bevelAndEmboss->texturePhase()); } w.leaveDescriptor(); } // Satin const psd_layer_effects_satin *satin = style->satin(); if (satin->effectEnabled()) { w.enterDescriptor("ChFX", "", "ChFX"); w.writeBoolean("enab", satin->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(satin->blendMode())); w.writeColor("Clr ", satin->color()); w.writeBoolean("AntA", satin->antiAliased()); w.writeBoolean("Invr", satin->invert()); w.writeUnitFloat("Opct", "#Prc", satin->opacity()); w.writeUnitFloat("lagl", "#Ang", satin->angle()); w.writeUnitFloat("Dstn", "#Pxl", satin->distance()); w.writeUnitFloat("blur", "#Pxl", satin->size()); // FIXME: save curves w.writeCurve("MpgS", "Linear", QVector() << QPointF() << QPointF(255, 255)); w.leaveDescriptor(); } const psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay(); if (colorOverlay->effectEnabled()) { w.enterDescriptor("SoFi", "", "SoFi"); w.writeBoolean("enab", colorOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(colorOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", colorOverlay->opacity()); w.writeColor("Clr ", colorOverlay->color()); w.leaveDescriptor(); } // Gradient Overlay const psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay(); KoSegmentGradient *segmentGradient = dynamic_cast(gradientOverlay->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(gradientOverlay->gradient().data()); if (gradientOverlay->effectEnabled() && (segmentGradient || stopGradient)) { w.enterDescriptor("GrFl", "", "GrFl"); w.writeBoolean("enab", gradientOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(gradientOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", gradientOverlay->opacity()); if (segmentGradient) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient) { w.writeStopGradient("Grad", stopGradient); } w.writeUnitFloat("Angl", "#Ang", gradientOverlay->angle()); w.writeEnum("Type", "GrdT", gradientTypeToString(gradientOverlay->style())); w.writeBoolean("Rvrs", gradientOverlay->reverse()); w.writeBoolean("Algn", gradientOverlay->alignWithLayer()); w.writeUnitFloat("Scl ", "#Prc", gradientOverlay->scale()); w.writeOffsetPoint("Ofst", gradientOverlay->gradientOffset()); // FIXME: Krita doesn't support dithering w.writeBoolean("Dthr", true/*gradientOverlay->dither()*/); w.leaveDescriptor(); } // Pattern Overlay const psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay(); if (patternOverlay->effectEnabled()) { w.enterDescriptor("patternFill", "", "patternFill"); w.writeBoolean("enab", patternOverlay->effectEnabled()); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(patternOverlay->blendMode())); w.writeUnitFloat("Opct", "#Prc", patternOverlay->opacity()); w.writePatternRef("Ptrn", patternOverlay->pattern(), fetchPatternUuidSafe(patternOverlay->pattern(), patternToUuidMap)); w.writeUnitFloat("Scl ", "#Prc", patternOverlay->scale()); w.writeBoolean("Algn", patternOverlay->alignWithLayer()); w.writePhasePoint("phase", patternOverlay->patternPhase()); w.leaveDescriptor(); } const psd_layer_effects_stroke *stroke = style->stroke(); if (stroke->effectEnabled()) { w.enterDescriptor("FrFX", "", "FrFX"); w.writeBoolean("enab", stroke->effectEnabled()); w.writeEnum("Styl", "FStl", strokePositionToString(stroke->position())); w.writeEnum("PntT", "FrFl", strokeFillTypeToString(stroke->fillType())); w.writeEnum("Md ", "BlnM", compositeOpToBlendMode(stroke->blendMode())); w.writeUnitFloat("Opct", "#Prc", stroke->opacity()); w.writeUnitFloat("Sz ", "#Pxl", stroke->size()); if (stroke->fillType() == psd_fill_solid_color) { w.writeColor("Clr ", stroke->color()); } else if (stroke->fillType() == psd_fill_gradient) { KoSegmentGradient *segmentGradient = dynamic_cast(stroke->gradient().data()); KoStopGradient *stopGradient = dynamic_cast(stroke->gradient().data()); if (segmentGradient) { w.writeSegmentGradient("Grad", segmentGradient); } else if (stopGradient) { w.writeStopGradient("Grad", stopGradient); } else { warnKrita << "WARNING: Stroke: Unknown gradient type!"; w.writeColor("Clr ", stroke->color()); } w.writeUnitFloat("Angl", "#Ang", stroke->angle()); w.writeEnum("Type", "GrdT", gradientTypeToString(stroke->style())); w.writeBoolean("Rvrs", stroke->reverse()); w.writeUnitFloat("Scl ", "#Prc", stroke->scale()); w.writeBoolean("Algn", stroke->alignWithLayer()); w.writeOffsetPoint("Ofst", stroke->gradientOffset()); // FIXME: Krita doesn't support dithering w.writeBoolean("Dthr", true/*stroke->dither()*/); } else if (stroke->fillType() == psd_fill_pattern) { w.writePatternRef("Ptrn", stroke->pattern(), fetchPatternUuidSafe(stroke->pattern(), patternToUuidMap)); w.writeUnitFloat("Scl ", "#Prc", stroke->scale()); w.writeBoolean("Lnkd", stroke->alignWithLayer()); w.writePhasePoint("phase", stroke->patternPhase()); } w.leaveDescriptor(); } w.leaveDescriptor(); w.leaveDescriptor(); } return w.document(); } inline QDomNode findNodeByClassId(const QString &classId, QDomNode parent) { return KisDomUtils::findElementByAttibute(parent, "node", "classId", classId); } void replaceAllChildren(QDomNode src, QDomNode dst) { QDomNode node; do { node = dst.lastChild(); dst.removeChild(node); } while(!node.isNull()); node = src.firstChild(); while(!node.isNull()) { dst.appendChild(node); node = src.firstChild(); } src.parentNode().removeChild(src); } QDomDocument KisAslLayerStyleSerializer::formPsdXmlDocument() const { QDomDocument doc = formXmlDocument(); QDomNode nullNode = findNodeByClassId("null", doc.documentElement()); QDomNode stylNode = findNodeByClassId("Styl", doc.documentElement()); QDomNode lefxNode = findNodeByClassId("Lefx", stylNode); replaceAllChildren(lefxNode, nullNode); return doc; } void KisAslLayerStyleSerializer::saveToDevice(QIODevice *device) { QDomDocument doc = formXmlDocument(); if (doc.isNull()) return ; KisAslWriter writer; writer.writeFile(device, doc); } void convertAndSetBlendMode(const QString &mode, boost::function setBlendMode) { QString compositeOp = COMPOSITE_OVER; if (mode == "Nrml") { compositeOp = COMPOSITE_OVER; } else if (mode == "Dslv") { compositeOp = COMPOSITE_DISSOLVE; } else if (mode == "Drkn") { compositeOp = COMPOSITE_DARKEN; } else if (mode == "Mltp") { compositeOp = COMPOSITE_MULT; } else if (mode == "CBrn") { compositeOp = COMPOSITE_BURN; } else if (mode == "linearBurn") { compositeOp = COMPOSITE_LINEAR_BURN; } else if (mode == "darkerColor") { compositeOp = COMPOSITE_DARKER_COLOR; } else if (mode == "Lghn") { compositeOp = COMPOSITE_LIGHTEN; } else if (mode == "Scrn") { compositeOp = COMPOSITE_SCREEN; } else if (mode == "CDdg") { compositeOp = COMPOSITE_DODGE; } else if (mode == "linearDodge") { compositeOp = COMPOSITE_LINEAR_DODGE; } else if (mode == "lighterColor") { compositeOp = COMPOSITE_LIGHTER_COLOR; } else if (mode == "Ovrl") { compositeOp = COMPOSITE_OVERLAY; } else if (mode == "SftL") { compositeOp = COMPOSITE_SOFT_LIGHT_PHOTOSHOP; } else if (mode == "HrdL") { compositeOp = COMPOSITE_HARD_LIGHT; } else if (mode == "vividLight") { compositeOp = COMPOSITE_VIVID_LIGHT; } else if (mode == "linearLight") { compositeOp = COMPOSITE_LINEAR_LIGHT; } else if (mode == "pinLight") { compositeOp = COMPOSITE_PIN_LIGHT; } else if (mode == "hardMix") { compositeOp = COMPOSITE_HARD_MIX; } else if (mode == "Dfrn") { compositeOp = COMPOSITE_DIFF; } else if (mode == "Xclu") { compositeOp = COMPOSITE_EXCLUSION; } else if (mode == "Sbtr") { compositeOp = COMPOSITE_SUBTRACT; } else if (mode == "divide") { compositeOp = COMPOSITE_DIVIDE; } else if (mode == "H ") { compositeOp = COMPOSITE_HUE; } else if (mode == "Strt") { compositeOp = COMPOSITE_SATURATION; } else if (mode == "Clr ") { compositeOp = COMPOSITE_COLOR; } else if (mode == "Lmns") { compositeOp = COMPOSITE_LUMINIZE; } else { dbgKrita << "Unknown blending mode:" << mode << "Returning COMPOSITE_OVER!"; } setBlendMode(compositeOp); } void convertAndSetCurve(const QString &name, const QVector &points, boost::function setCurveLookupTable) { Q_UNUSED(name); Q_UNUSED(points); Q_UNUSED(setCurveLookupTable); warnKrita << "convertAndSetBlendMode:" << "Curve conversion is not implemented yet"; } template void convertAndSetEnum(const QString &value, const QMap map, boost::function setMappedValue) { setMappedValue(map[value]); } inline QString _prepaddr(const QString &pref, const QString &addr) { return pref + addr; } #define CONN_TEXT_RADDR(addr, method, object, type) m_catcher.subscribeText(addr, boost::bind(&type::method, object, _1)) #define CONN_COLOR(addr, method, object, type, prefix) m_catcher.subscribeColor(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1)) #define CONN_UNITF(addr, unit, method, object, type, prefix) m_catcher.subscribeUnitFloat(_prepaddr(prefix, addr), unit, boost::bind(&type::method, object, _1)) #define CONN_BOOL(addr, method, object, type, prefix) m_catcher.subscribeBoolean(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1)) #define CONN_POINT(addr, method, object, type, prefix) m_catcher.subscribePoint(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1)) #define CONN_COMPOSITE_OP(addr, method, object, type, prefix) \ { \ boost::function setter = \ boost::bind(&type::method, object, _1); \ m_catcher.subscribeEnum(_prepaddr(prefix, addr), "BlnM", boost::bind(convertAndSetBlendMode, _1, setter)); \ } #define CONN_CURVE(addr, method, object, type, prefix) \ { \ boost::function setter = \ boost::bind(&type::method, object, _1); \ m_catcher.subscribeCurve(_prepaddr(prefix, addr), boost::bind(convertAndSetCurve, _1, _2, setter)); \ } #define CONN_ENUM(addr, tag, method, map, mapped_type, object, type, prefix) \ { \ boost::function setter = \ boost::bind(&type::method, object, _1); \ m_catcher.subscribeEnum(_prepaddr(prefix, addr), tag, boost::bind(convertAndSetEnum, _1, map, setter)); \ } #define CONN_GRADIENT(addr, method, object, type, prefix) \ { \ m_catcher.subscribeGradient(_prepaddr(prefix, addr), boost::bind(&type::method, object, _1)); \ } #define CONN_PATTERN(addr, method, object, type, prefix) \ { \ boost::function setter = \ boost::bind(&type::method, object, _1); \ m_catcher.subscribePatternRef(_prepaddr(prefix, addr), boost::bind(&KisAslLayerStyleSerializer::assignPatternObject, this, _1, _2, setter)); \ } void KisAslLayerStyleSerializer::registerPatternObject(const KoPattern *pattern) { QString uuid = KisAslWriterUtils::getPatternUuidLazy(pattern); if (m_patternsStore.contains(uuid)) { warnKrita << "WARNING: ASL style contains a duplicated pattern!" << ppVar(pattern->name()) << ppVar(m_patternsStore[uuid]->name()); } else { KoResourceServer *server = KoResourceServerProvider::instance()->patternServer(); KoPattern *patternToAdd = server->resourceByMD5(pattern->md5()); if (!patternToAdd) { patternToAdd = pattern->clone(); server->addResource(patternToAdd, false); } m_patternsStore.insert(uuid, patternToAdd); } } void KisAslLayerStyleSerializer::assignPatternObject(const QString &patternUuid, const QString &patternName, boost::function setPattern) { Q_UNUSED(patternName); KoPattern *pattern = m_patternsStore[patternUuid]; if (!pattern) { warnKrita << "WARNING: ASL style contains inexistent pattern reference!"; QImage dumbImage(32, 32, QImage::Format_ARGB32); dumbImage.fill(Qt::red); KoPattern *dumbPattern = new KoPattern(dumbImage, "invalid", ""); registerPatternObject(dumbPattern); pattern = dumbPattern; } setPattern(pattern); } class FillStylesCorrector { public: static void correct(KisPSDLayerStyle *style) { correctWithoutPattern(style->outerGlow()); correctWithoutPattern(style->innerGlow()); correctWithPattern(style->stroke()); } private: template static void correctWithPattern(T *config) { if (config->pattern()) { config->setFillType(psd_fill_pattern); } else if (config->gradient()) { config->setFillType(psd_fill_gradient); } else { config->setFillType(psd_fill_solid_color); } } template static void correctWithoutPattern(T *config) { if (config->gradient()) { config->setFillType(psd_fill_gradient); } else { config->setFillType(psd_fill_solid_color); } } }; void KisAslLayerStyleSerializer::connectCatcherToStyle(KisPSDLayerStyle *style, const QString &prefix) { CONN_TEXT_RADDR("/null/Nm ", setName, style, KisPSDLayerStyle); CONN_TEXT_RADDR("/null/Idnt", setPsdUuid, style, KisPSDLayerStyle); CONN_BOOL("/masterFXSwitch", setEnabled, style, KisPSDLayerStyle, prefix); psd_layer_effects_drop_shadow *dropShadow = style->dropShadow(); CONN_COMPOSITE_OP("/DrSh/Md ", setBlendMode, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_COLOR("/DrSh/Clr ", setColor, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Opct", "#Prc", setOpacity, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/lagl", "#Ang", setAngle, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Dstn", "#Pxl", setDistance, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Ckmt", "#Pxl", setSpread, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/blur", "#Pxl", setSize, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_UNITF("/DrSh/Nose", "#Prc", setNoise, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/enab", setEffectEnabled, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/uglg", setUseGlobalLight, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/AntA", setAntiAliased, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_BOOL("/DrSh/layerConceals", setKnocksOut, dropShadow, psd_layer_effects_drop_shadow, prefix); CONN_CURVE("/DrSh/TrnS", setContourLookupTable, dropShadow, psd_layer_effects_drop_shadow, prefix); psd_layer_effects_inner_shadow *innerShadow = style->innerShadow(); CONN_COMPOSITE_OP("/IrSh/Md ", setBlendMode, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_COLOR("/IrSh/Clr ", setColor, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Opct", "#Prc", setOpacity, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/lagl", "#Ang", setAngle, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Dstn", "#Pxl", setDistance, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Ckmt", "#Pxl", setSpread, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/blur", "#Pxl", setSize, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_UNITF("/IrSh/Nose", "#Prc", setNoise, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/enab", setEffectEnabled, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/uglg", setUseGlobalLight, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_BOOL("/IrSh/AntA", setAntiAliased, innerShadow, psd_layer_effects_inner_shadow, prefix); CONN_CURVE("/IrSh/TrnS", setContourLookupTable, innerShadow, psd_layer_effects_inner_shadow, prefix); psd_layer_effects_outer_glow *outerGlow = style->outerGlow(); CONN_COMPOSITE_OP("/OrGl/Md ", setBlendMode, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_COLOR("/OrGl/Clr ", setColor, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Opct", "#Prc", setOpacity, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Ckmt", "#Pxl", setSpread, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/blur", "#Pxl", setSize, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Nose", "#Prc", setNoise, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_BOOL("/OrGl/enab", setEffectEnabled, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_BOOL("/OrGl/AntA", setAntiAliased, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_CURVE("/OrGl/TrnS", setContourLookupTable, outerGlow, psd_layer_effects_outer_glow, prefix); QMap fillTechniqueMap; fillTechniqueMap.insert("PrBL", psd_technique_precise); fillTechniqueMap.insert("SfBL", psd_technique_softer); CONN_ENUM("/OrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_GRADIENT("/OrGl/Grad", setGradient, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/Inpr", "#Prc", setRange, outerGlow, psd_layer_effects_outer_glow, prefix); CONN_UNITF("/OrGl/ShdN", "#Prc", setJitter, outerGlow, psd_layer_effects_outer_glow, prefix); psd_layer_effects_inner_glow *innerGlow = style->innerGlow(); CONN_COMPOSITE_OP("/IrGl/Md ", setBlendMode, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_COLOR("/IrGl/Clr ", setColor, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Opct", "#Prc", setOpacity, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Ckmt", "#Pxl", setSpread, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/blur", "#Pxl", setSize, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Nose", "#Prc", setNoise, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_BOOL("/IrGl/enab", setEffectEnabled, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_BOOL("/IrGl/AntA", setAntiAliased, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_CURVE("/IrGl/TrnS", setContourLookupTable, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_ENUM("/IrGl/GlwT", "BETE", setTechnique, fillTechniqueMap, psd_technique_type, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_GRADIENT("/IrGl/Grad", setGradient, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/Inpr", "#Prc", setRange, innerGlow, psd_layer_effects_inner_glow, prefix); CONN_UNITF("/IrGl/ShdN", "#Prc", setJitter, innerGlow, psd_layer_effects_inner_glow, prefix); QMap glowSourceMap; glowSourceMap.insert("SrcC", psd_glow_center); glowSourceMap.insert("SrcE", psd_glow_edge); CONN_ENUM("/IrGl/glwS", "IGSr", setSource, glowSourceMap, psd_glow_source, innerGlow, psd_layer_effects_inner_glow, prefix); psd_layer_effects_satin *satin = style->satin(); CONN_COMPOSITE_OP("/ChFX/Md ", setBlendMode, satin, psd_layer_effects_satin, prefix); CONN_COLOR("/ChFX/Clr ", setColor, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/Opct", "#Prc", setOpacity, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/lagl", "#Ang", setAngle, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/Dstn", "#Pxl", setDistance, satin, psd_layer_effects_satin, prefix); CONN_UNITF("/ChFX/blur", "#Pxl", setSize, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/enab", setEffectEnabled, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/AntA", setAntiAliased, satin, psd_layer_effects_satin, prefix); CONN_BOOL("/ChFX/Invr", setInvert, satin, psd_layer_effects_satin, prefix); CONN_CURVE("/ChFX/MpgS", setContourLookupTable, satin, psd_layer_effects_satin, prefix); psd_layer_effects_color_overlay *colorOverlay = style->colorOverlay(); CONN_COMPOSITE_OP("/SoFi/Md ", setBlendMode, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_COLOR("/SoFi/Clr ", setColor, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_UNITF("/SoFi/Opct", "#Prc", setOpacity, colorOverlay, psd_layer_effects_color_overlay, prefix); CONN_BOOL("/SoFi/enab", setEffectEnabled, colorOverlay, psd_layer_effects_color_overlay, prefix); psd_layer_effects_gradient_overlay *gradientOverlay = style->gradientOverlay(); CONN_COMPOSITE_OP("/GrFl/Md ", setBlendMode, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Opct", "#Prc", setOpacity, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Scl ", "#Prc", setScale, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_UNITF("/GrFl/Angl", "#Ang", setAngle, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/enab", setEffectEnabled, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); // CONN_BOOL("/GrFl/Dthr", setDitherNotImplemented, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/Rvrs", setReverse, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_BOOL("/GrFl/Algn", setAlignWithLayer, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_POINT("/GrFl/Ofst", setGradientOffset, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); CONN_GRADIENT("/GrFl/Grad", setGradient, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); QMap gradientStyleMap; gradientStyleMap.insert("Lnr ", psd_gradient_style_linear); gradientStyleMap.insert("Rdl ", psd_gradient_style_radial); gradientStyleMap.insert("Angl", psd_gradient_style_angle); gradientStyleMap.insert("Rflc", psd_gradient_style_reflected); gradientStyleMap.insert("Dmnd", psd_gradient_style_diamond); CONN_ENUM("/GrFl/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, gradientOverlay, psd_layer_effects_gradient_overlay, prefix); psd_layer_effects_pattern_overlay *patternOverlay = style->patternOverlay(); CONN_BOOL("/patternFill/enab", setEffectEnabled, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_COMPOSITE_OP("/patternFill/Md ", setBlendMode, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_UNITF("/patternFill/Opct", "#Prc", setOpacity, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_PATTERN("/patternFill/Ptrn", setPattern, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_UNITF("/patternFill/Scl ", "#Prc", setScale, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_BOOL("/patternFill/Algn", setAlignWithLayer, patternOverlay, psd_layer_effects_pattern_overlay, prefix); CONN_POINT("/patternFill/phase", setPatternPhase, patternOverlay, psd_layer_effects_pattern_overlay, prefix); psd_layer_effects_stroke *stroke = style->stroke(); CONN_COMPOSITE_OP("/FrFX/Md ", setBlendMode, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/enab", setEffectEnabled, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Opct", "#Prc", setOpacity, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Sz ", "#Pxl", setSize, stroke, psd_layer_effects_stroke, prefix); QMap strokeStyleMap; strokeStyleMap.insert("OutF", psd_stroke_outside); strokeStyleMap.insert("InsF", psd_stroke_inside); strokeStyleMap.insert("CtrF", psd_stroke_center); CONN_ENUM("/FrFX/Styl", "FStl", setPosition, strokeStyleMap, psd_stroke_position, stroke, psd_layer_effects_stroke, prefix); QMap strokeFillType; strokeFillType.insert("SClr", psd_fill_solid_color); strokeFillType.insert("GrFl", psd_fill_gradient); strokeFillType.insert("Ptrn", psd_fill_pattern); CONN_ENUM("/FrFX/PntT", "FrFl", setFillType, strokeFillType, psd_fill_type, stroke, psd_layer_effects_stroke, prefix); // Color type CONN_COLOR("/FrFX/Clr ", setColor, stroke, psd_layer_effects_stroke, prefix); // Gradient Type CONN_GRADIENT("/FrFX/Grad", setGradient, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Angl", "#Ang", setAngle, stroke, psd_layer_effects_stroke, prefix); CONN_UNITF("/FrFX/Scl ", "#Prc", setScale, stroke, psd_layer_effects_stroke, prefix); CONN_ENUM("/FrFX/Type", "GrdT", setStyle, gradientStyleMap, psd_gradient_style, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Rvrs", setReverse, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Algn", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); CONN_POINT("/FrFX/Ofst", setGradientOffset, stroke, psd_layer_effects_stroke, prefix); // CONN_BOOL("/FrFX/Dthr", setDitherNotImplemented, stroke, psd_layer_effects_stroke, prefix); // Pattern type CONN_PATTERN("/FrFX/Ptrn", setPattern, stroke, psd_layer_effects_stroke, prefix); CONN_BOOL("/FrFX/Lnkd", setAlignWithLayer, stroke, psd_layer_effects_stroke, prefix); // yes, we share the params... CONN_POINT("/FrFX/phase", setPatternPhase, stroke, psd_layer_effects_stroke, prefix); psd_layer_effects_bevel_emboss *bevelAndEmboss = style->bevelAndEmboss(); CONN_BOOL("/ebbl/enab", setEffectEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COMPOSITE_OP("/ebbl/hglM", setHighlightBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COLOR("/ebbl/hglC", setHighlightColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/hglO", "#Prc", setHighlightOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COMPOSITE_OP("/ebbl/sdwM", setShadowBlendMode, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_COLOR("/ebbl/sdwC", setShadowColor, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/sdwO", "#Prc", setShadowOpacity, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelTechniqueMap; bevelTechniqueMap.insert("PrBL", psd_technique_precise); bevelTechniqueMap.insert("SfBL", psd_technique_softer); bevelTechniqueMap.insert("Slmt", psd_technique_slope_limit); CONN_ENUM("/ebbl/bvlT", "bvlT", setTechnique, bevelTechniqueMap, psd_technique_type, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelStyleMap; bevelStyleMap.insert("OtrB", psd_bevel_outer_bevel); bevelStyleMap.insert("InrB", psd_bevel_inner_bevel); bevelStyleMap.insert("Embs", psd_bevel_emboss); bevelStyleMap.insert("PlEb", psd_bevel_pillow_emboss); bevelStyleMap.insert("strokeEmboss", psd_bevel_stroke_emboss); CONN_ENUM("/ebbl/bvlS", "BESl", setStyle, bevelStyleMap, psd_bevel_style, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/uglg", setUseGlobalLight, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/lagl", "#Ang", setAngle, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Lald", "#Ang", setAltitude, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/srgR", "#Prc", setDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/blur", "#Pxl", setSize, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); QMap bevelDirectionMap; bevelDirectionMap.insert("In ", psd_direction_up); bevelDirectionMap.insert("Out ", psd_direction_down); CONN_ENUM("/ebbl/bvlD", "BESs", setDirection, bevelDirectionMap, psd_direction, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_CURVE("/ebbl/TrnS", setContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/antialiasGloss", setGlossAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Sftn", "#Pxl", setSoften, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); // Use shape mode CONN_BOOL("/ebbl/useShape", setContourEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_CURVE("/ebbl/MpgS", setGlossContourLookupTable, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/AntA", setAntiAliased, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Inpr", "#Prc", setContourRange, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); // Use texture mode CONN_BOOL("/ebbl/useTexture", setTextureEnabled, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/InvT", setTextureInvert, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_BOOL("/ebbl/Algn", setTextureAlignWithLayer, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/Scl ", "#Prc", setTextureScale, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_UNITF("/ebbl/textureDepth", "#Prc", setTextureDepth, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_PATTERN("/ebbl/Ptrn", setTexturePattern, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); CONN_POINT("/ebbl/phase", setTexturePhase, bevelAndEmboss, psd_layer_effects_bevel_emboss, prefix); } void KisAslLayerStyleSerializer::newStyleStarted(bool isPsdStructure) { m_stylesVector.append(toQShared(new KisPSDLayerStyle())); KisPSDLayerStyle *currentStyle = m_stylesVector.last().data(); psd_layer_effects_context *context = currentStyle->context(); context->keep_original = 0; QString prefix = isPsdStructure ? "/null" : "/Styl/Lefx"; connectCatcherToStyle(currentStyle, prefix); } void KisAslLayerStyleSerializer::readFromDevice(QIODevice *device) { m_stylesVector.clear(); m_catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); m_catcher.subscribeNewStyleStarted(boost::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, false)); KisAslReader reader; QDomDocument doc = reader.readFile(device); //dbgKrita << ppVar(doc.toString()); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, m_catcher); // correct all the layer styles foreach(KisPSDLayerStyleSP style, m_stylesVector) { FillStylesCorrector::correct(style.data()); } } void KisAslLayerStyleSerializer::registerPSDPattern(const QDomDocument &doc) { KisAslCallbackObjectCatcher catcher; catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, catcher); } void KisAslLayerStyleSerializer::readFromPSDXML(const QDomDocument &doc) { // The caller prepares the document using th efollowing code // // KisAslReader reader; // QDomDocument doc = reader.readLfx2PsdSection(device); m_stylesVector.clear(); //m_catcher.subscribePattern("/Patterns/KisPattern", boost::bind(&KisAslLayerStyleSerializer::registerPatternObject, this, _1)); m_catcher.subscribeNewStyleStarted(boost::bind(&KisAslLayerStyleSerializer::newStyleStarted, this, true)); //KisAslObjectCatcher c2; KisAslXmlParser parser; parser.parseXML(doc, m_catcher); // correct all the layer styles foreach(KisPSDLayerStyleSP style, m_stylesVector) { FillStylesCorrector::correct(style.data()); } } diff --git a/krita/ui/kis_autogradient.h b/krita/ui/kis_autogradient.h index 8a25f969f1..2228fdc315 100644 --- a/krita/ui/kis_autogradient.h +++ b/krita/ui/kis_autogradient.h @@ -1,51 +1,50 @@ /* * Copyright (c) 2004 Cyrille Berger * 2004 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_AUTOGRADIENT_H_ #define _KIS_AUTOGRADIENT_H_ #include "ui_wdgautogradient.h" -class KoResource; class KoGradientSegment; class KoSegmentGradient; class KisAutogradient : public QWidget, public Ui::KisWdgAutogradient { Q_OBJECT public: KisAutogradient(KoSegmentGradient* gradient, QWidget *parent, const char* name, const QString& caption); void activate(); private: KoSegmentGradient* m_autogradientResource; private Q_SLOTS: void slotSelectedSegment(KoGradientSegment* segment); void slotChangedSegment(KoGradientSegment* segment); void slotChangedInterpolation(int type); void slotChangedColorInterpolation(int type); void slotChangedLeftColor(const QColor& color); void slotChangedRightColor(const QColor& color); void slotChangedLeftOpacity(int value); void slotChangedRightOpacity(int value); void slotChangedName(); void paramChanged(); }; #endif diff --git a/krita/ui/kis_clipboard.h b/krita/ui/kis_clipboard.h index 5520202fc8..45afefbd61 100644 --- a/krita/ui/kis_clipboard.h +++ b/krita/ui/kis_clipboard.h @@ -1,94 +1,93 @@ /* * kis_clipboard.h - part of Krayon * * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_CLIPBOARD_H_ #define __KIS_CLIPBOARD_H_ #include #include #include "kis_types.h" #include -class QImage; class QRect; enum enumPasteBehaviour { PASTE_ASSUME_WEB, PASTE_ASSUME_MONITOR, PASTE_ASK }; /** * The Krita clipboard is a clipboard that can store paint devices * instead of just qimage's. */ class KRITAUI_EXPORT KisClipboard : public QObject { Q_OBJECT Q_PROPERTY(bool clip READ hasClip NOTIFY clipChanged) public: KisClipboard(); virtual ~KisClipboard(); static KisClipboard* instance(); /** * Sets the clipboard to the contents of the specified paint device; also * set the system clipboard to a QImage representation of the specified * paint device. * * @param dev The paint device that will be stored on the clipboard * @param topLeft a hint about the place where the clip should be pasted by default */ void setClip(KisPaintDeviceSP dev, const QPoint& topLeft); /** * Get the contents of the clipboard in the form of a paint device. */ KisPaintDeviceSP clip(const QRect &imageBounds, bool showPopup); bool hasClip() const; QSize clipSize() const; Q_SIGNALS: void clipCreated(); private Q_SLOTS: void clipboardDataChanged(); private: KisClipboard(const KisClipboard &); KisClipboard operator=(const KisClipboard &); bool m_hasClip; bool m_pushedClipboard; Q_SIGNALS: void clipChanged(); }; #endif // __KIS_CLIPBOARD_H_ diff --git a/krita/ui/kis_config.cc b/krita/ui/kis_config.cc index 2e70249436..5012692ccd 100644 --- a/krita/ui/kis_config.cc +++ b/krita/ui/kis_config.cc @@ -1,1604 +1,1603 @@ /* * Copyright (c) 2002 Patrick Julien * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_canvas_resource_provider.h" -#include "kis_global.h" #include "kis_config_notifier.h" #include #include KisConfig::KisConfig() : m_cfg( KSharedConfig::openConfig()->group("")) { } KisConfig::~KisConfig() { if (qApp->thread() != QThread::currentThread()) { //dbgKrita << "WARNING: KisConfig: requested config synchronization from nonGUI thread! Skipping..."; return; } m_cfg.sync(); } bool KisConfig::disableTouchOnCanvas(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("disableTouchOnCanvas", false)); } void KisConfig::setDisableTouchOnCanvas(bool value) const { m_cfg.writeEntry("disableTouchOnCanvas", value); } bool KisConfig::useProjections(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useProjections", true)); } void KisConfig::setUseProjections(bool useProj) const { m_cfg.writeEntry("useProjections", useProj); } bool KisConfig::undoEnabled(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("undoEnabled", true)); } void KisConfig::setUndoEnabled(bool undo) const { m_cfg.writeEntry("undoEnabled", undo); } int KisConfig::undoStackLimit(bool defaultValue) const { return (defaultValue ? 30 : m_cfg.readEntry("undoStackLimit", 30)); } void KisConfig::setUndoStackLimit(int limit) const { m_cfg.writeEntry("undoStackLimit", limit); } bool KisConfig::useCumulativeUndoRedo(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useCumulativeUndoRedo",false)); } void KisConfig::setCumulativeUndoRedo(bool value) { m_cfg.writeEntry("useCumulativeUndoRedo", value); } double KisConfig::stackT1(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackT1",5)); } void KisConfig::setStackT1(int T1) { m_cfg.writeEntry("stackT1", T1); } double KisConfig::stackT2(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("stackT2",1)); } void KisConfig::setStackT2(int T2) { m_cfg.writeEntry("stackT2", T2); } int KisConfig::stackN(bool defaultValue) const { return (defaultValue ? 5 : m_cfg.readEntry("stackN",5)); } void KisConfig::setStackN(int N) { m_cfg.writeEntry("stackN", N); } qint32 KisConfig::defImageWidth(bool defaultValue) const { return (defaultValue ? 1600 : m_cfg.readEntry("imageWidthDef", 1600)); } qint32 KisConfig::defImageHeight(bool defaultValue) const { return (defaultValue ? 1200 : m_cfg.readEntry("imageHeightDef", 1200)); } double KisConfig::defImageResolution(bool defaultValue) const { return (defaultValue ? 100.0 : m_cfg.readEntry("imageResolutionDef", 100.0)) / 72.0; } QString KisConfig::defColorModel(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id() : m_cfg.readEntry("colorModelDef", KoColorSpaceRegistry::instance()->rgb8()->colorModelId().id())); } void KisConfig::defColorModel(const QString & model) const { m_cfg.writeEntry("colorModelDef", model); } QString KisConfig::defaultColorDepth(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id() : m_cfg.readEntry("colorDepthDef", KoColorSpaceRegistry::instance()->rgb8()->colorDepthId().id())); } void KisConfig::setDefaultColorDepth(const QString & depth) const { m_cfg.writeEntry("colorDepthDef", depth); } QString KisConfig::defColorProfile(bool defaultValue) const { return (defaultValue ? KoColorSpaceRegistry::instance()->rgb8()->profile()->name() : m_cfg.readEntry("colorProfileDef", KoColorSpaceRegistry::instance()->rgb8()->profile()->name())); } void KisConfig::defColorProfile(const QString & profile) const { m_cfg.writeEntry("colorProfileDef", profile); } void KisConfig::defImageWidth(qint32 width) const { m_cfg.writeEntry("imageWidthDef", width); } void KisConfig::defImageHeight(qint32 height) const { m_cfg.writeEntry("imageHeightDef", height); } void KisConfig::defImageResolution(double res) const { m_cfg.writeEntry("imageResolutionDef", res*72.0); } void cleanOldCursorStyleKeys(KConfigGroup &cfg) { if (cfg.hasKey("newCursorStyle") && cfg.hasKey("newOutlineStyle")) { cfg.deleteEntry("cursorStyleDef"); } } CursorStyle KisConfig::newCursorStyle(bool defaultValue) const { if (defaultValue) { return CURSOR_STYLE_NO_CURSOR; } int style = m_cfg.readEntry("newCursorStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: style = CURSOR_STYLE_TOOLICON; break; case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: style = CURSOR_STYLE_CROSSHAIR; break; case OLD_CURSOR_STYLE_POINTER: style = CURSOR_STYLE_POINTER; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_NO_CURSOR: style = CURSOR_STYLE_NO_CURSOR; break; case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: style = CURSOR_STYLE_SMALL_ROUND; break; case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: style = CURSOR_STYLE_TRIANGLE_RIGHTHANDED; break; case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = CURSOR_STYLE_TRIANGLE_LEFTHANDED; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_CURSOR_STYLE_SIZE) { style = CURSOR_STYLE_NO_CURSOR; } return (CursorStyle) style; } void KisConfig::setNewCursorStyle(CursorStyle style) { m_cfg.writeEntry("newCursorStyle", (int)style); } OutlineStyle KisConfig::newOutlineStyle(bool defaultValue) const { if (defaultValue) { return OUTLINE_FULL; } int style = m_cfg.readEntry("newOutlineStyle", int(-1)); if (style < 0) { // old style format style = m_cfg.readEntry("cursorStyleDef", int(OLD_CURSOR_STYLE_OUTLINE)); switch (style) { case OLD_CURSOR_STYLE_TOOLICON: case OLD_CURSOR_STYLE_CROSSHAIR: case OLD_CURSOR_STYLE_POINTER: case OLD_CURSOR_STYLE_NO_CURSOR: case OLD_CURSOR_STYLE_SMALL_ROUND: case OLD_CURSOR_STYLE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_TRIANGLE_LEFTHANDED: style = OUTLINE_NONE; break; case OLD_CURSOR_STYLE_OUTLINE: case OLD_CURSOR_STYLE_OUTLINE_CENTER_DOT: case OLD_CURSOR_STYLE_OUTLINE_CENTER_CROSS: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_RIGHTHANDED: case OLD_CURSOR_STYLE_OUTLINE_TRIANGLE_LEFTHANDED: style = OUTLINE_FULL; break; default: style = -1; } } cleanOldCursorStyleKeys(m_cfg); // compatibility with future versions if (style < 0 || style >= N_OUTLINE_STYLE_SIZE) { style = OUTLINE_FULL; } return (OutlineStyle) style; } void KisConfig::setNewOutlineStyle(OutlineStyle style) { m_cfg.writeEntry("newOutlineStyle", (int)style); } QRect KisConfig::colorPreviewRect() const { return m_cfg.readEntry("colorPreviewRect", QVariant(QRect(32, 32, 48, 48))).toRect(); } void KisConfig::setColorPreviewRect(const QRect &rect) { m_cfg.writeEntry("colorPreviewRect", QVariant(rect)); } bool KisConfig::useDirtyPresets(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useDirtyPresets",false)); } void KisConfig::setUseDirtyPresets(bool value) { m_cfg.writeEntry("useDirtyPresets",value); KisConfigNotifier::instance()->notifyConfigChanged(); } bool KisConfig::useEraserBrushSize(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useEraserBrushSize",false)); } void KisConfig::setUseEraserBrushSize(bool value) { m_cfg.writeEntry("useEraserBrushSize",value); KisConfigNotifier::instance()->notifyConfigChanged(); } QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const { QColor col(77, 77, 77); return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col)); } void KisConfig::setMDIBackgroundColor(const QColor &v) const { m_cfg.writeEntry("mdiBackgroundColor", v); } QString KisConfig::getMDIBackgroundImage(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("mdiBackgroundImage", "")); } void KisConfig::setMDIBackgroundImage(const QString &filename) const { m_cfg.writeEntry("mdiBackgroundImage", filename); } QString KisConfig::monitorProfile(int screen) const { // Note: keep this in sync with the default profile for the RGB colorspaces! QString profile = m_cfg.readEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), "sRGB-elle-V2-srgbtrc.icc"); //dbgKrita << "KisConfig::monitorProfile()" << profile; return profile; } QString KisConfig::monitorForScreen(int screen, const QString &defaultMonitor, bool defaultValue) const { return (defaultValue ? defaultMonitor : m_cfg.readEntry(QString("monitor_for_screen_%1").arg(screen), defaultMonitor)); } void KisConfig::setMonitorForScreen(int screen, const QString& monitor) { m_cfg.writeEntry(QString("monitor_for_screen_%1").arg(screen), monitor); } void KisConfig::setMonitorProfile(int screen, const QString & monitorProfile, bool override) const { m_cfg.writeEntry("monitorProfile/OverrideX11", override); m_cfg.writeEntry("monitorProfile" + QString(screen == 0 ? "": QString("_%1").arg(screen)), monitorProfile); } const KoColorProfile *KisConfig::getScreenProfile(int screen) { KisConfig cfg; QString monitorId; if (KisColorManager::instance()->devices().size() > screen) { monitorId = cfg.monitorForScreen(screen, KisColorManager::instance()->devices()[screen]); } //dbgKrita << "getScreenProfile(). Screen" << screen << "monitor id" << monitorId; if (monitorId.isEmpty()) { return 0; } QByteArray bytes = KisColorManager::instance()->displayProfile(monitorId); //dbgKrita << "\tgetScreenProfile()" << bytes.size(); if (bytes.length() > 0) { const KoColorProfile *profile = KoColorSpaceRegistry::instance()->createColorProfile(RGBAColorModelID.id(), Integer8BitsColorDepthID.id(), bytes); //dbgKrita << "\tKisConfig::getScreenProfile for screen" << screen << profile->name(); return profile; } else { //dbgKrita << "\tCould not get a system monitor profile"; return 0; } } const KoColorProfile *KisConfig::displayProfile(int screen) const { // if the user plays with the settings, they can override the display profile, in which case // we don't want the system setting. bool override = useSystemMonitorProfile(); //dbgKrita << "KisConfig::displayProfile(). Override X11:" << override; const KoColorProfile *profile = 0; if (override) { //dbgKrita << "\tGoing to get the screen profile"; profile = KisConfig::getScreenProfile(screen); } // if it fails. check the configuration if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tGoing to get the monitor profile"; QString monitorProfileName = monitorProfile(screen); //dbgKrita << "\t\tmonitorProfileName:" << monitorProfileName; if (!monitorProfileName.isEmpty()) { profile = KoColorSpaceRegistry::instance()->profileByName(monitorProfileName); } if (profile) { //dbgKrita << "\t\tsuitable for display" << profile->isSuitableForDisplay(); } else { //dbgKrita << "\t\tstill no profile"; } } // if we still don't have a profile, or the profile isn't suitable for display, // we need to get a last-resort profile. the built-in sRGB is a good choice then. if (!profile || !profile->isSuitableForDisplay()) { //dbgKrita << "\tnothing worked, going to get sRGB built-in"; profile = KoColorSpaceRegistry::instance()->profileByName("sRGB Built-in"); } if (profile) { //dbgKrita << "\tKisConfig::displayProfile for screen" << screen << "is" << profile->name(); } else { //dbgKrita << "\tCouldn't get a display profile at all"; } return profile; } QString KisConfig::workingColorSpace(bool defaultValue) const { return (defaultValue ? "RGBA" : m_cfg.readEntry("workingColorSpace", "RGBA")); } void KisConfig::setWorkingColorSpace(const QString & workingColorSpace) const { m_cfg.writeEntry("workingColorSpace", workingColorSpace); } QString KisConfig::printerColorSpace(bool /*defaultValue*/) const { //TODO currently only rgb8 is supported //return (defaultValue ? "RGBA" : m_cfg.readEntry("printerColorSpace", "RGBA")); return QString("RGBA"); } void KisConfig::setPrinterColorSpace(const QString & printerColorSpace) const { m_cfg.writeEntry("printerColorSpace", printerColorSpace); } QString KisConfig::printerProfile(bool defaultValue) const { return (defaultValue ? "" : m_cfg.readEntry("printerProfile", "")); } void KisConfig::setPrinterProfile(const QString & printerProfile) const { m_cfg.writeEntry("printerProfile", printerProfile); } bool KisConfig::useBlackPointCompensation(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useBlackPointCompensation", true)); } void KisConfig::setUseBlackPointCompensation(bool useBlackPointCompensation) const { m_cfg.writeEntry("useBlackPointCompensation", useBlackPointCompensation); } bool KisConfig::allowLCMSOptimization(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("allowLCMSOptimization", true)); } void KisConfig::setAllowLCMSOptimization(bool allowLCMSOptimization) { m_cfg.writeEntry("allowLCMSOptimization", allowLCMSOptimization); } bool KisConfig::showRulers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showrulers", false)); } void KisConfig::setShowRulers(bool rulers) const { m_cfg.writeEntry("showrulers", rulers); } qint32 KisConfig::pasteBehaviour(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("pasteBehaviour", 2)); } void KisConfig::setPasteBehaviour(qint32 renderIntent) const { m_cfg.writeEntry("pasteBehaviour", renderIntent); } qint32 KisConfig::monitorRenderIntent(bool defaultValue) const { qint32 intent = m_cfg.readEntry("renderIntent", INTENT_PERCEPTUAL); if (intent > 3) intent = 3; if (intent < 0) intent = 0; return (defaultValue ? INTENT_PERCEPTUAL : intent); } void KisConfig::setRenderIntent(qint32 renderIntent) const { if (renderIntent > 3) renderIntent = 3; if (renderIntent < 0) renderIntent = 0; m_cfg.writeEntry("renderIntent", renderIntent); } bool KisConfig::useOpenGL(bool defaultValue) const { if (qApp->applicationName() == "krita") { if (defaultValue) { #ifdef Q_WS_MAC return false; #else return true; #endif } //dbgKrita << "use opengl" << m_cfg.readEntry("useOpenGL", true) << "success" << m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); QString canvasState = m_cfg.readEntry("canvasState", "OPENGL_SUCCESS"); #ifdef Q_WS_MAC return (m_cfg.readEntry("useOpenGL", false) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); #else return (m_cfg.readEntry("useOpenGL", true) && (canvasState == "OPENGL_SUCCESS" || canvasState == "TRY_OPENGL")); #endif } else if (qApp->applicationName() == "kritasketch" || qApp->applicationName() == "kritagemini") { return true; // for sketch and gemini } else { return false; } } void KisConfig::setUseOpenGL(bool useOpenGL) const { m_cfg.writeEntry("useOpenGL", useOpenGL); } int KisConfig::openGLFilteringMode(bool defaultValue) const { return (defaultValue ? 3 : m_cfg.readEntry("OpenGLFilterMode", 3)); } void KisConfig::setOpenGLFilteringMode(int filteringMode) { m_cfg.writeEntry("OpenGLFilterMode", filteringMode); } bool KisConfig::useOpenGLTextureBuffer(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("useOpenGLTextureBuffer", true)); } void KisConfig::setUseOpenGLTextureBuffer(bool useBuffer) { m_cfg.writeEntry("useOpenGLTextureBuffer", useBuffer); } int KisConfig::openGLTextureSize(bool defaultValue) const { return (defaultValue ? 256 : m_cfg.readEntry("textureSize", 256)); } bool KisConfig::disableDoubleBuffering(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableDoubleBuffering", true)); } void KisConfig::setDisableDoubleBuffering(bool disableDoubleBuffering) { m_cfg.writeEntry("disableDoubleBuffering", disableDoubleBuffering); } bool KisConfig::disableVSync(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("disableVSync", true)); } void KisConfig::setDisableVSync(bool disableVSync) { m_cfg.writeEntry("disableVSync", disableVSync); } bool KisConfig::showAdvancedOpenGLSettings(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showAdvancedOpenGLSettings", false)); } bool KisConfig::forceOpenGLFenceWorkaround(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("forceOpenGLFenceWorkaround", false)); } int KisConfig::numMipmapLevels(bool defaultValue) const { return (defaultValue ? 4 : m_cfg.readEntry("numMipmapLevels", 4)); } int KisConfig::textureOverlapBorder() const { return 1 << qMax(0, numMipmapLevels()); } qint32 KisConfig::maxNumberOfThreads(bool defaultValue) const { return (defaultValue ? QThread::idealThreadCount() : m_cfg.readEntry("maxthreads", QThread::idealThreadCount())); } void KisConfig::setMaxNumberOfThreads(qint32 maxThreads) { m_cfg.writeEntry("maxthreads", maxThreads); } quint32 KisConfig::getGridMainStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridmainstyle", 0); if (v > 2) v = 2; return (defaultValue ? 0 : v); } void KisConfig::setGridMainStyle(quint32 v) const { m_cfg.writeEntry("gridmainstyle", v); } quint32 KisConfig::getGridSubdivisionStyle(bool defaultValue) const { quint32 v = m_cfg.readEntry("gridsubdivisionstyle", 1); if (v > 2) v = 2; return (defaultValue ? 1 : v); } void KisConfig::setGridSubdivisionStyle(quint32 v) const { m_cfg.writeEntry("gridsubdivisionstyle", v); } QColor KisConfig::getGridMainColor(bool defaultValue) const { QColor col(99, 99, 99); return (defaultValue ? col : m_cfg.readEntry("gridmaincolor", col)); } void KisConfig::setGridMainColor(const QColor & v) const { m_cfg.writeEntry("gridmaincolor", v); } QColor KisConfig::getGridSubdivisionColor(bool defaultValue) const { QColor col(150, 150, 150); return (defaultValue ? col : m_cfg.readEntry("gridsubdivisioncolor", col)); } void KisConfig::setGridSubdivisionColor(const QColor & v) const { m_cfg.writeEntry("gridsubdivisioncolor", v); } quint32 KisConfig::getGridHSpacing(bool defaultValue) const { qint32 v = m_cfg.readEntry("gridhspacing", 10); return (defaultValue ? 10 : (quint32)qMax(1, v)); } void KisConfig::setGridHSpacing(quint32 v) const { m_cfg.writeEntry("gridhspacing", v); } quint32 KisConfig::getGridVSpacing(bool defaultValue) const { qint32 v = m_cfg.readEntry("gridvspacing", 10); return (defaultValue ? 10 : (quint32)qMax(1, v)); } void KisConfig::setGridVSpacing(quint32 v) const { m_cfg.writeEntry("gridvspacing", v); } bool KisConfig::getGridSpacingAspect(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("gridspacingaspect", false)); } void KisConfig::setGridSpacingAspect(bool v) const { m_cfg.writeEntry("gridspacingaspect", v); } quint32 KisConfig::getGridSubdivisions(bool defaultValue) const { qint32 v = m_cfg.readEntry("gridsubsivisons", 2); return (defaultValue ? 2 : (quint32)qMax(1, v)); } void KisConfig::setGridSubdivisions(quint32 v) const { m_cfg.writeEntry("gridsubsivisons", v); } quint32 KisConfig::getGridOffsetX(bool defaultValue) const { qint32 v = m_cfg.readEntry("gridoffsetx", 0); return (defaultValue ? 0 : (quint32)qMax(0, v)); } void KisConfig::setGridOffsetX(quint32 v) const { m_cfg.writeEntry("gridoffsetx", v); } quint32 KisConfig::getGridOffsetY(bool defaultValue) const { qint32 v = m_cfg.readEntry("gridoffsety", 0); return (defaultValue ? 0 : (quint32)qMax(0, v)); } void KisConfig::setGridOffsetY(quint32 v) const { m_cfg.writeEntry("gridoffsety", v); } bool KisConfig::getGridOffsetAspect(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("gridoffsetaspect", false)); } void KisConfig::setGridOffsetAspect(bool v) const { m_cfg.writeEntry("gridoffsetaspect", v); } qint32 KisConfig::checkSize(bool defaultValue) const { return (defaultValue ? 32 : m_cfg.readEntry("checksize", 32)); } void KisConfig::setCheckSize(qint32 checksize) const { m_cfg.writeEntry("checksize", checksize); } bool KisConfig::scrollCheckers(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("scrollingcheckers", false)); } void KisConfig::setScrollingCheckers(bool sc) const { m_cfg.writeEntry("scrollingcheckers", sc); } QColor KisConfig::canvasBorderColor(bool defaultValue) const { QColor color(QColor(128,128,128)); return (defaultValue ? color : m_cfg.readEntry("canvasBorderColor", color)); } void KisConfig::setCanvasBorderColor(const QColor& color) const { m_cfg.writeEntry("canvasBorderColor", color); } bool KisConfig::hideScrollbars(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hideScrollbars", false)); } void KisConfig::setHideScrollbars(bool value) const { m_cfg.writeEntry("hideScrollbars", value); } QColor KisConfig::checkersColor1(bool defaultValue) const { QColor col(220, 220, 220); return (defaultValue ? col : m_cfg.readEntry("checkerscolor", col)); } void KisConfig::setCheckersColor1(const QColor & v) const { m_cfg.writeEntry("checkerscolor", v); } QColor KisConfig::checkersColor2(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("checkerscolor2", QColor(Qt::white))); } void KisConfig::setCheckersColor2(const QColor & v) const { m_cfg.writeEntry("checkerscolor2", v); } bool KisConfig::antialiasCurves(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("antialiascurves", true)); } void KisConfig::setAntialiasCurves(bool v) const { m_cfg.writeEntry("antialiascurves", v); } QColor KisConfig::selectionOverlayMaskColor(bool defaultValue) const { QColor def(255, 0, 0, 220); return (defaultValue ? def : m_cfg.readEntry("selectionOverlayMaskColor", def)); } void KisConfig::setSelectionOverlayMaskColor(const QColor &color) { m_cfg.writeEntry("selectionOverlayMaskColor", color); } bool KisConfig::antialiasSelectionOutline(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("AntialiasSelectionOutline", false)); } void KisConfig::setAntialiasSelectionOutline(bool v) const { m_cfg.writeEntry("AntialiasSelectionOutline", v); } bool KisConfig::showRootLayer(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowRootLayer", false)); } void KisConfig::setShowRootLayer(bool showRootLayer) const { m_cfg.writeEntry("ShowRootLayer", showRootLayer); } bool KisConfig::showGlobalSelection(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ShowGlobalSelection", false)); } void KisConfig::setShowGlobalSelection(bool showGlobalSelection) const { m_cfg.writeEntry("ShowGlobalSelection", showGlobalSelection); } bool KisConfig::showOutlineWhilePainting(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ShowOutlineWhilePainting", true)); } void KisConfig::setShowOutlineWhilePainting(bool showOutlineWhilePainting) const { m_cfg.writeEntry("ShowOutlineWhilePainting", showOutlineWhilePainting); } bool KisConfig::hideSplashScreen(bool defaultValue) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); return (defaultValue ? true : cfg.readEntry("HideSplashAfterStartup", true)); } void KisConfig::setHideSplashScreen(bool hideSplashScreen) const { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); cfg.writeEntry("HideSplashAfterStartup", hideSplashScreen); } qreal KisConfig::outlineSizeMinimum(bool defaultValue) const { return (defaultValue ? 1.0 : m_cfg.readEntry("OutlineSizeMinimum", 1.0)); } void KisConfig::setOutlineSizeMinimum(qreal outlineSizeMinimum) const { m_cfg.writeEntry("OutlineSizeMinimum", outlineSizeMinimum); } int KisConfig::autoSaveInterval(bool defaultValue) const { return (defaultValue ? KisDocument::defaultAutoSave() : m_cfg.readEntry("AutoSaveInterval", KisDocument::defaultAutoSave())); } void KisConfig::setAutoSaveInterval(int seconds) const { return m_cfg.writeEntry("AutoSaveInterval", seconds); } bool KisConfig::backupFile(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("CreateBackupFile", true)); } void KisConfig::setBackupFile(bool backupFile) const { m_cfg.writeEntry("CreateBackupFile", backupFile); } bool KisConfig::showFilterGallery(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showFilterGallery", false)); } void KisConfig::setShowFilterGallery(bool showFilterGallery) const { m_cfg.writeEntry("showFilterGallery", showFilterGallery); } bool KisConfig::showFilterGalleryLayerMaskDialog(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showFilterGalleryLayerMaskDialog", true)); } void KisConfig::setShowFilterGalleryLayerMaskDialog(bool showFilterGallery) const { m_cfg.writeEntry("setShowFilterGalleryLayerMaskDialog", showFilterGallery); } QString KisConfig::canvasState(bool defaultValue) const { return (defaultValue ? "OPENGL_NOT_TRIED" : m_cfg.readEntry("canvasState", "OPENGL_NOT_TRIED")); } void KisConfig::setCanvasState(const QString& state) const { static QStringList acceptableStates; if (acceptableStates.isEmpty()) { acceptableStates << "OPENGL_SUCCESS" << "TRY_OPENGL" << "OPENGL_NOT_TRIED" << "OPENGL_FAILED"; } if (acceptableStates.contains(state)) { m_cfg.writeEntry("canvasState", state); m_cfg.sync(); } } bool KisConfig::toolOptionsPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ToolOptionsPopupDetached", false)); } void KisConfig::setToolOptionsPopupDetached(bool detached) const { m_cfg.writeEntry("ToolOptionsPopupDetached", detached); } bool KisConfig::paintopPopupDetached(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("PaintopPopupDetached", false)); } void KisConfig::setPaintopPopupDetached(bool detached) const { m_cfg.writeEntry("PaintopPopupDetached", detached); } QString KisConfig::pressureTabletCurve(bool defaultValue) const { return (defaultValue ? "0,0;1,1" : m_cfg.readEntry("tabletPressureCurve","0,0;1,1;")); } void KisConfig::setPressureTabletCurve(const QString& curveString) const { m_cfg.writeEntry("tabletPressureCurve", curveString); } qreal KisConfig::vastScrolling(bool defaultValue) const { return (defaultValue ? 0.9 : m_cfg.readEntry("vastScrolling", 0.9)); } void KisConfig::setVastScrolling(const qreal factor) const { m_cfg.writeEntry("vastScrolling", factor); } int KisConfig::presetChooserViewMode(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("presetChooserViewMode", 0)); } void KisConfig::setPresetChooserViewMode(const int mode) const { m_cfg.writeEntry("presetChooserViewMode", mode); } bool KisConfig::firstRun(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("firstRun", true)); } void KisConfig::setFirstRun(const bool first) const { m_cfg.writeEntry("firstRun", first); } int KisConfig::horizontalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("horizontalSplitLines", 1)); } void KisConfig::setHorizontalSplitLines(const int numberLines) const { m_cfg.writeEntry("horizontalSplitLines", numberLines); } int KisConfig::verticalSplitLines(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("verticalSplitLines", 1)); } void KisConfig::setVerticalSplitLines(const int numberLines) const { m_cfg.writeEntry("verticalSplitLines", numberLines); } bool KisConfig::clicklessSpacePan(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("clicklessSpacePan", true)); } void KisConfig::setClicklessSpacePan(const bool toggle) const { m_cfg.writeEntry("clicklessSpacePan", toggle); } bool KisConfig::hideDockersFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideDockersFullScreen", true)); } void KisConfig::setHideDockersFullscreen(const bool value) const { m_cfg.writeEntry("hideDockersFullScreen", value); } bool KisConfig::showDockerTitleBars(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showDockerTitleBars", true)); } void KisConfig::setShowDockerTitleBars(const bool value) const { m_cfg.writeEntry("showDockerTitleBars", value); } bool KisConfig::hideMenuFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideMenuFullScreen", true)); } void KisConfig::setHideMenuFullscreen(const bool value) const { m_cfg.writeEntry("hideMenuFullScreen", value); } bool KisConfig::hideScrollbarsFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideScrollbarsFullScreen", true)); } void KisConfig::setHideScrollbarsFullscreen(const bool value) const { m_cfg.writeEntry("hideScrollbarsFullScreen", value); } bool KisConfig::hideStatusbarFullscreen(bool defaultValue) const { return (defaultValue ? true: m_cfg.readEntry("hideStatusbarFullScreen", true)); } void KisConfig::setHideStatusbarFullscreen(const bool value) const { m_cfg.writeEntry("hideStatusbarFullScreen", value); } bool KisConfig::hideTitlebarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideTitleBarFullscreen", true)); } void KisConfig::setHideTitlebarFullscreen(const bool value) const { m_cfg.writeEntry("hideTitleBarFullscreen", value); } bool KisConfig::hideToolbarFullscreen(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("hideToolbarFullscreen", true)); } void KisConfig::setHideToolbarFullscreen(const bool value) const { m_cfg.writeEntry("hideToolbarFullscreen", value); } bool KisConfig::fullscreenMode(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("fullscreenMode", true)); } void KisConfig::setFullscreenMode(const bool value) const { m_cfg.writeEntry("fullscreenMode", value); } QStringList KisConfig::favoriteCompositeOps(bool defaultValue) const { return (defaultValue ? QStringList() : m_cfg.readEntry("favoriteCompositeOps", QStringList())); } void KisConfig::setFavoriteCompositeOps(const QStringList& compositeOps) const { m_cfg.writeEntry("favoriteCompositeOps", compositeOps); } QString KisConfig::exportConfiguration(const QString &filterId, bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("ExportConfiguration-" + filterId, QString())); } void KisConfig::setExportConfiguration(const QString &filterId, const KisPropertiesConfiguration &properties) const { QString exportConfig = properties.toXML(); m_cfg.writeEntry("ExportConfiguration-" + filterId, exportConfig); } bool KisConfig::useOcio(bool defaultValue) const { #ifdef HAVE_OCIO return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/UseOcio", false)); #else return false; #endif } void KisConfig::setUseOcio(bool useOCIO) const { m_cfg.writeEntry("Krita/Ocio/UseOcio", useOCIO); } int KisConfig::favoritePresets(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("numFavoritePresets", 10)); } void KisConfig::setFavoritePresets(const int value) { m_cfg.writeEntry("numFavoritePresets", value); } KisConfig::OcioColorManagementMode KisConfig::ocioColorManagementMode(bool defaultValue) const { return (OcioColorManagementMode)(defaultValue ? INTERNAL : m_cfg.readEntry("Krita/Ocio/OcioColorManagementMode", (int) INTERNAL)); } void KisConfig::setOcioColorManagementMode(OcioColorManagementMode mode) const { m_cfg.writeEntry("Krita/Ocio/OcioColorManagementMode", (int) mode); } QString KisConfig::ocioConfigurationPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioConfigPath", QString())); } void KisConfig::setOcioConfigurationPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioConfigPath", path); } QString KisConfig::ocioLutPath(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("Krita/Ocio/OcioLutPath", QString())); } void KisConfig::setOcioLutPath(const QString &path) const { m_cfg.writeEntry("Krita/Ocio/OcioLutPath", path); } int KisConfig::ocioLutEdgeSize(bool defaultValue) const { return (defaultValue ? 64 : m_cfg.readEntry("Krita/Ocio/LutEdgeSize", 64)); } void KisConfig::setOcioLutEdgeSize(int value) { m_cfg.writeEntry("Krita/Ocio/LutEdgeSize", value); } bool KisConfig::ocioLockColorVisualRepresentation(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("Krita/Ocio/OcioLockColorVisualRepresentation", false)); } void KisConfig::setOcioLockColorVisualRepresentation(bool value) { m_cfg.writeEntry("Krita/Ocio/OcioLockColorVisualRepresentation", value); } QString KisConfig::defaultPalette(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("defaultPalette", QString())); } void KisConfig::setDefaultPalette(const QString& name) const { m_cfg.writeEntry("defaultPalette", name); } QString KisConfig::toolbarSlider(int sliderNumber, bool defaultValue) const { QString def = "flow"; if (sliderNumber == 1) { def = "opacity"; } if (sliderNumber == 2) { def = "size"; } return (defaultValue ? def : m_cfg.readEntry(QString("toolbarslider_%1").arg(sliderNumber), def)); } void KisConfig::setToolbarSlider(int sliderNumber, const QString &slider) { m_cfg.writeEntry(QString("toolbarslider_%1").arg(sliderNumber), slider); } bool KisConfig::sliderLabels(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("sliderLabels", true)); } void KisConfig::setSliderLabels(bool enabled) { m_cfg.writeEntry("sliderLabels", enabled); } QString KisConfig::currentInputProfile(bool defaultValue) const { return (defaultValue ? QString() : m_cfg.readEntry("currentInputProfile", QString())); } void KisConfig::setCurrentInputProfile(const QString& name) { m_cfg.writeEntry("currentInputProfile", name); } bool KisConfig::useSystemMonitorProfile(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("ColorManagement/UseSystemMonitorProfile", false)); } void KisConfig::setUseSystemMonitorProfile(bool _useSystemMonitorProfile) const { m_cfg.writeEntry("ColorManagement/UseSystemMonitorProfile", _useSystemMonitorProfile); } bool KisConfig::presetStripVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("presetStripVisible", true)); } void KisConfig::setPresetStripVisible(bool visible) { m_cfg.writeEntry("presetStripVisible", visible); } bool KisConfig::scratchpadVisible(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("scratchpadVisible", true)); } void KisConfig::setScratchpadVisible(bool visible) { m_cfg.writeEntry("scratchpadVisible", visible); } bool KisConfig::showSingleChannelAsColor(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("showSingleChannelAsColor", false)); } void KisConfig::setShowSingleChannelAsColor(bool asColor) { m_cfg.writeEntry("showSingleChannelAsColor", asColor); } bool KisConfig::hidePopups(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("hidePopups", false)); } void KisConfig::setHidePopups(bool hidepopups) { m_cfg.writeEntry("hidePopups", hidepopups); } int KisConfig::numDefaultLayers(bool defaultValue) const { return (defaultValue ? 2 : m_cfg.readEntry("NumberOfLayersForNewImage", 2)); } void KisConfig::setNumDefaultLayers(int num) { m_cfg.writeEntry("NumberOfLayersForNewImage", num); } quint8 KisConfig::defaultBackgroundOpacity(bool defaultValue) const { return (defaultValue ? (int)OPACITY_OPAQUE_U8 : m_cfg.readEntry("BackgroundOpacityForNewImage", (int)OPACITY_OPAQUE_U8)); } void KisConfig::setDefaultBackgroundOpacity(quint8 value) { m_cfg.writeEntry("BackgroundOpacityForNewImage", (int)value); } QColor KisConfig::defaultBackgroundColor(bool defaultValue) const { return (defaultValue ? QColor(Qt::white) : m_cfg.readEntry("BackgroundColorForNewImage", QColor(Qt::white))); } void KisConfig::setDefaultBackgroundColor(QColor value) { m_cfg.writeEntry("BackgroundColorForNewImage", value); } KisConfig::BackgroundStyle KisConfig::defaultBackgroundStyle(bool defaultValue) const { return (KisConfig::BackgroundStyle)(defaultValue ? LAYER : m_cfg.readEntry("BackgroundStyleForNewImage", (int)LAYER)); } void KisConfig::setDefaultBackgroundStyle(KisConfig::BackgroundStyle value) { m_cfg.writeEntry("BackgroundStyleForNewImage", (int)value); } int KisConfig::lineSmoothingType(bool defaultValue) const { return (defaultValue ? 1 : m_cfg.readEntry("LineSmoothingType", 1)); } void KisConfig::setLineSmoothingType(int value) { m_cfg.writeEntry("LineSmoothingType", value); } qreal KisConfig::lineSmoothingDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDistance", 50.0)); } void KisConfig::setLineSmoothingDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDistance", value); } qreal KisConfig::lineSmoothingTailAggressiveness(bool defaultValue) const { return (defaultValue ? 0.15 : m_cfg.readEntry("LineSmoothingTailAggressiveness", 0.15)); } void KisConfig::setLineSmoothingTailAggressiveness(qreal value) { m_cfg.writeEntry("LineSmoothingTailAggressiveness", value); } bool KisConfig::lineSmoothingSmoothPressure(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("LineSmoothingSmoothPressure", false)); } void KisConfig::setLineSmoothingSmoothPressure(bool value) { m_cfg.writeEntry("LineSmoothingSmoothPressure", value); } bool KisConfig::lineSmoothingScalableDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingScalableDistance", true)); } void KisConfig::setLineSmoothingScalableDistance(bool value) { m_cfg.writeEntry("LineSmoothingScalableDistance", value); } qreal KisConfig::lineSmoothingDelayDistance(bool defaultValue) const { return (defaultValue ? 50.0 : m_cfg.readEntry("LineSmoothingDelayDistance", 50.0)); } void KisConfig::setLineSmoothingDelayDistance(qreal value) { m_cfg.writeEntry("LineSmoothingDelayDistance", value); } bool KisConfig::lineSmoothingUseDelayDistance(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingUseDelayDistance", true)); } void KisConfig::setLineSmoothingUseDelayDistance(bool value) { m_cfg.writeEntry("LineSmoothingUseDelayDistance", value); } bool KisConfig::lineSmoothingFinishStabilizedCurve(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingFinishStabilizedCurve", true)); } void KisConfig::setLineSmoothingFinishStabilizedCurve(bool value) { m_cfg.writeEntry("LineSmoothingFinishStabilizedCurve", value); } bool KisConfig::lineSmoothingStabilizeSensors(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("LineSmoothingStabilizeSensors", true)); } void KisConfig::setLineSmoothingStabilizeSensors(bool value) { m_cfg.writeEntry("LineSmoothingStabilizeSensors", value); } int KisConfig::paletteDockerPaletteViewSectionSize(bool defaultValue) const { return (defaultValue ? 12 : m_cfg.readEntry("paletteDockerPaletteViewSectionSize", 12)); } void KisConfig::setPaletteDockerPaletteViewSectionSize(int value) const { m_cfg.writeEntry("paletteDockerPaletteViewSectionSize", value); } int KisConfig::tabletEventsDelay(bool defaultValue) const { return (defaultValue ? 10 : m_cfg.readEntry("tabletEventsDelay", 10)); } void KisConfig::setTabletEventsDelay(int value) { m_cfg.writeEntry("tabletEventsDelay", value); } bool KisConfig::testingAcceptCompressedTabletEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingAcceptCompressedTabletEvents", false)); } void KisConfig::setTestingAcceptCompressedTabletEvents(bool value) { m_cfg.writeEntry("testingAcceptCompressedTabletEvents", value); } bool KisConfig::testingCompressBrushEvents(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("testingCompressBrushEvents", false)); } void KisConfig::setTestingCompressBrushEvents(bool value) { m_cfg.writeEntry("testingCompressBrushEvents", value); } bool KisConfig::useVerboseOpenGLDebugOutput(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("useVerboseOpenGLDebugOutput", false)); } int KisConfig::workaroundX11SmoothPressureSteps(bool defaultValue) const { return (defaultValue ? 0 : m_cfg.readEntry("workaroundX11SmoothPressureSteps", 0)); } bool KisConfig::showCanvasMessages(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("showOnCanvasMessages", true)); } void KisConfig::setShowCanvasMessages(bool show) { m_cfg.writeEntry("showOnCanvasMessages", show); } bool KisConfig::compressKra(bool defaultValue) const { return (defaultValue ? false : m_cfg.readEntry("compressLayersInKra", false)); } void KisConfig::setCompressKra(bool compress) { m_cfg.writeEntry("compressLayersInKra", compress); } bool KisConfig::toolOptionsInDocker(bool defaultValue) const { return (defaultValue ? true : m_cfg.readEntry("ToolOptionsInDocker", true)); } void KisConfig::setToolOptionsInDocker(bool inDocker) { m_cfg.writeEntry("ToolOptionsInDocker", inDocker); } const KoColorSpace* KisConfig::customColorSelectorColorSpace(bool defaultValue) const { const KoColorSpace *cs = 0; KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); if (defaultValue || cfg.readEntry("useCustomColorSpace", true)) { KoColorSpaceRegistry* csr = KoColorSpaceRegistry::instance(); cs = csr->colorSpace(cfg.readEntry("customColorSpaceModel", "RGBA"), cfg.readEntry("customColorSpaceDepthID", "U8"), cfg.readEntry("customColorSpaceProfile", "sRGB built-in - (lcms internal)")); } return cs; } void KisConfig::setCustomColorSelectorColorSpace(const KoColorSpace *cs) { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); cfg.writeEntry("useCustomColorSpace", bool(cs)); if(cs) { cfg.writeEntry("customColorSpaceModel", cs->colorModelId().id()); cfg.writeEntry("customColorSpaceDepthID", cs->colorDepthId().id()); cfg.writeEntry("customColorSpaceProfile", cs->profile()->name()); } KisConfigNotifier::instance()->notifyConfigChanged(); } diff --git a/krita/ui/kis_histogram_view.cc b/krita/ui/kis_histogram_view.cc index 1490a858ac..ade2d45ee4 100644 --- a/krita/ui/kis_histogram_view.cc +++ b/krita/ui/kis_histogram_view.cc @@ -1,381 +1,379 @@ /* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_histogram_view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoChannelInfo.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" -#include "kis_histogram.h" #include "kis_global.h" -#include "kis_types.h" #include "kis_layer.h" #include "kis_paint_device.h" KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WFlags f) : QLabel(parent, f) { setObjectName(name); // This is needed until we can computationally scale it well. Until then, this is needed // And when we have it, it won't hurt to have it around setScaledContents(true); setFrameShape(QFrame::Box); // Draw a box around ourselves } KisHistogramView::~KisHistogramView() { } void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) { m_cs = dev->colorSpace(); setChannels(); // Sets m_currentProducer to the first in the list if (!m_currentProducer) return; m_from = m_currentProducer->viewFrom(); m_width = m_currentProducer->viewWidth(); m_histogram = new KisHistogram(dev, bounds, m_currentProducer, LINEAR); updateHistogram(); } void KisHistogramView::setHistogram(KisHistogramSP histogram) { if (!histogram) return; m_cs = 0; m_histogram = histogram; m_currentProducer = m_histogram->producer(); m_from = m_currentProducer->viewFrom(); m_width = m_currentProducer->viewWidth(); m_comboInfo.clear(); m_channelStrings.clear(); m_channels.clear(); m_channelToOffset.clear(); addProducerChannels(m_currentProducer); // Set the currently viewed channel: m_color = false; m_channels.append(m_comboInfo.at(1).channel); m_channelToOffset.append(0); updateHistogram(); } void KisHistogramView::setView(double from, double size) { m_from = from; m_width = size; if (m_from + m_width > 1.0) m_from = 1.0 - m_width; m_histogram->producer()->setView(m_from, m_width); m_histogram->updateHistogram(); updateHistogram(); } KoHistogramProducer *KisHistogramView::currentProducer() { return m_currentProducer; } QStringList KisHistogramView::channelStrings() { return m_channelStrings; } QList KisHistogramView::producers() { if (m_cs) return KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); return QList(); } void KisHistogramView::setCurrentChannels(const KoID& producerID, QList channels) { setCurrentChannels( KoHistogramProducerFactoryRegistry::instance()->value(producerID.id())->generate(), channels); } void KisHistogramView::setCurrentChannels(KoHistogramProducer *producer, QList channels) { m_currentProducer = producer; m_currentProducer->setView(m_from, m_width); m_histogram->setProducer(m_currentProducer); m_histogram->updateHistogram(); m_histogram->setChannel(0); // Set a default channel, just being nice m_channels.clear(); m_channelToOffset.clear(); if (channels.count() == 0) { updateHistogram(); return; } QList producerChannels = m_currentProducer->channels(); for (int i = 0; i < channels.count(); i++) { // Also makes sure the channel is actually in the producer's list for (int j = 0; j < producerChannels.count(); j++) { if (channels.at(i)->name() == producerChannels.at(j)->name()) { m_channelToOffset.append(m_channels.count()); // The first we append maps to 0 m_channels.append(channels.at(i)); } } } updateHistogram(); } bool KisHistogramView::hasColor() { return m_color; } void KisHistogramView::setColor(bool set) { if (set != m_color) { m_color = set; updateHistogram(); } } void KisHistogramView::setActiveChannel(int channel) { ComboboxInfo info = m_comboInfo.at(channel); if (info.producer != m_currentProducer) { m_currentProducer = info.producer; m_currentProducer->setView(m_from, m_width); m_histogram->setProducer(m_currentProducer); m_histogram->updateHistogram(); } m_channels.clear(); m_channelToOffset.clear(); if (!m_currentProducer) { updateHistogram(); return; } if (info.isProducer) { m_color = true; m_channels = m_currentProducer->channels(); for (int i = 0; i < m_channels.count(); i++) m_channelToOffset.append(i); m_histogram->setChannel(0); // Set a default channel, just being nice } else { m_color = false; QList channels = m_currentProducer->channels(); for (int i = 0; i < channels.count(); i++) { KoChannelInfo* channel = channels.at(i); if (channel->name() == info.channel->name()) { m_channels.append(channel); m_channelToOffset.append(i); break; } } } updateHistogram(); } void KisHistogramView::setHistogramType(enumHistogramType type) { m_histogram->setHistogramType(type); updateHistogram(); } void KisHistogramView::setChannels() { m_comboInfo.clear(); m_channelStrings.clear(); m_channels.clear(); m_channelToOffset.clear(); QList list = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); if (list.count() == 0) { // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning KoGenericRGBHistogramProducerFactory f; addProducerChannels(f.generate()); } else { foreach (const QString &id, list) { KoHistogramProducer *producer = KoHistogramProducerFactoryRegistry::instance()->value(id)->generate(); if (producer) { addProducerChannels(producer); } } } m_currentProducer = m_comboInfo.at(0).producer; m_color = false; // The currently displayed channel and its offset m_channels.append(m_comboInfo.at(1).channel); m_channelToOffset.append(0); } void KisHistogramView::addProducerChannels(KoHistogramProducer *producer) { if (!producer) return; ComboboxInfo info; info.isProducer = true; info.producer = producer; // channel not used for a producer QList channels = info.producer->channels(); int count = channels.count(); m_comboInfo.append(info); m_channelStrings.append(producer->id() . name()); for (int j = 0; j < count; j++) { info.isProducer = false; info.channel = channels.at(j); m_comboInfo.append(info); m_channelStrings.append(QString(" ").append(info.channel->name())); } } void KisHistogramView::updateHistogram() { quint32 height = this->height(); int selFrom, selTo; // from - to in bins if (!m_currentProducer) { // Something's very wrong: no producer for this colorspace to update histogram with! return; } qint32 bins = m_histogram->producer()->numberOfBins(); m_pix = QPixmap(bins, height); m_pix.fill(); QPainter p(&m_pix); p.setBrush(Qt::black); // Draw the box of the selection, if any if (m_histogram->hasSelection()) { double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); selFrom = static_cast(m_histogram->selectionFrom() * factor); selTo = selFrom + static_cast(width * factor); p.drawRect(selFrom, 0, selTo - selFrom, height); } else { // We don't want the drawing to think we're in a selected area selFrom = -1; selTo = 2; } qint32 i = 0; double highest = 0; bool blackOnBlack = false; // First we iterate once, so that we have the overall maximum. This is a bit inefficient, // but not too much since the histogram caches the calculations for (int chan = 0; chan < m_channels.count(); chan++) { m_histogram->setChannel(m_channelToOffset.at(chan)); if ((double)m_histogram->calculations().getHighest() > highest) highest = (double)m_histogram->calculations().getHighest(); } QPen pen(Qt::white); for (int chan = 0; chan < m_channels.count(); chan++) { QColor color; m_histogram->setChannel(m_channelToOffset.at(chan)); if (m_color) { color = m_channels.at(chan)->color(); QLinearGradient gradient; gradient.setColorAt(0, Qt::white); gradient.setColorAt(1, color); QBrush brush(gradient); pen = QPen(brush, 0); } else { color = Qt::black; } blackOnBlack = (color == Qt::black); if (m_histogram->getHistogramType() == LINEAR) { double factor = (double)height / highest; for (i = 0; i < bins; ++i) { // So that we get a good view even with a selection box with // black colors on background of black selection if (i >= selFrom && i < selTo && blackOnBlack) { p.setPen(Qt::white); } else { p.setPen(pen); } p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); } } else { double factor = (double)height / (double)log(highest); for (i = 0; i < bins; ++i) { // Same as above if (i >= selFrom && i < selTo && blackOnBlack) { p.setPen(Qt::white); } else { p.setPen(pen); } if (m_histogram->getValue(i) > 0) { // Don't try to calculate log(0) p.drawLine(i, height, i, height - int(log((double)m_histogram->getValue(i)) * factor)); } } } } setPixmap(m_pix); } void KisHistogramView::mousePressEvent(QMouseEvent * e) { if (e->button() == Qt::RightButton) emit rightClicked(e->globalPos()); else QLabel::mousePressEvent(e); } diff --git a/krita/ui/kis_import_catcher.cc b/krita/ui/kis_import_catcher.cc index 135d1df740..97e433195e 100644 --- a/krita/ui/kis_import_catcher.cc +++ b/krita/ui/kis_import_catcher.cc @@ -1,130 +1,129 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_import_catcher.h" #include #include #include #include #include #include "kis_node_manager.h" -#include "kis_types.h" #include "kis_count_visitor.h" #include "KisViewManager.h" #include "KisDocument.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_painter.h" #include "kis_selection.h" #include "kis_node_commands_adapter.h" #include "kis_group_layer.h" #include "kis_statusbar.h" #include "kis_progress_widget.h" #include "KisPart.h" struct KisImportCatcher::Private { public: KisDocument* doc; KisViewManager* view; QUrl url; QString layerType; QString prettyLayerName() const; void importAsPaintLayer(KisPaintDeviceSP device); void importAsTransparencyMask(KisPaintDeviceSP device); }; QString KisImportCatcher::Private::prettyLayerName() const { QString name = url.fileName(); return !name.isEmpty() ? name : url.toDisplayString(); } void KisImportCatcher::Private::importAsPaintLayer(KisPaintDeviceSP device) { KisLayerSP newLayer = new KisPaintLayer(view->image(), prettyLayerName(), OPACITY_OPAQUE_U8, device); KisNodeSP parent = 0; KisLayerSP currentActiveLayer = view->activeLayer(); if (currentActiveLayer) { parent = currentActiveLayer->parent(); } if (parent.isNull()) { parent = view->image()->rootLayer(); } KisNodeCommandsAdapter adapter(view); adapter.addNode(newLayer, parent, currentActiveLayer); } KisImportCatcher::KisImportCatcher(const QUrl &url, KisViewManager * view, const QString &layerType) : m_d(new Private) { m_d->doc = KisPart::instance()->createDocument(); KoProgressProxy *progressProxy = view->statusBar()->progress()->progressProxy(); m_d->doc->setProgressProxy(progressProxy); m_d->view = view; m_d->url = url; m_d->layerType = layerType; connect(m_d->doc, SIGNAL(sigLoadingFinished()), this, SLOT(slotLoadingFinished())); bool result = m_d->doc->openUrl(url); if (!result) { deleteMyself(); } } void KisImportCatcher::slotLoadingFinished() { KisImageWSP importedImage = m_d->doc->image(); importedImage->waitForDone(); if (importedImage && importedImage->projection()->exactBounds().isValid()) { if (m_d->layerType != "KisPaintLayer") { m_d->view->nodeManager()->createNode(m_d->layerType, false, importedImage->projection()); } else { m_d->importAsPaintLayer(importedImage->projection()); } } deleteMyself(); } void KisImportCatcher::deleteMyself() { m_d->doc->deleteLater(); deleteLater(); } KisImportCatcher::~KisImportCatcher() { delete m_d; } diff --git a/krita/ui/kis_layer_manager.cc b/krita/ui/kis_layer_manager.cc index 572add2e4b..f1788fee51 100644 --- a/krita/ui/kis_layer_manager.cc +++ b/krita/ui/kis_layer_manager.cc @@ -1,1007 +1,1006 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_layer_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "KisView.h" #include "kis_config.h" #include "kis_cursor.h" #include "dialogs/kis_dlg_adj_layer_props.h" #include "dialogs/kis_dlg_adjustment_layer.h" #include "dialogs/kis_dlg_layer_properties.h" #include "dialogs/kis_dlg_generator_layer.h" #include "dialogs/kis_dlg_file_layer.h" #include "dialogs/kis_dlg_layer_style.h" #include "KisDocument.h" #include "kis_filter_manager.h" #include "kis_node_visitor.h" #include "kis_paint_layer.h" #include "commands/kis_image_commands.h" #include "commands/kis_layer_commands.h" #include "commands/kis_node_commands.h" #include "kis_canvas_resource_provider.h" #include "kis_selection_manager.h" #include "kis_statusbar.h" #include "KisViewManager.h" #include "kis_zoom_manager.h" #include "canvas/kis_canvas2.h" #include "widgets/kis_meta_data_merge_strategy_chooser_widget.h" #include "widgets/kis_wdg_generator.h" #include "kis_progress_widget.h" #include "kis_node_commands_adapter.h" #include "kis_node_manager.h" #include "kis_action.h" #include "kis_action_manager.h" #include "KisPart.h" #include "kis_signal_compressor_with_param.h" #include "kis_abstract_projection_plane.h" #include "commands_new/kis_set_layer_style_command.h" #include "kis_post_execution_undo_adapter.h" #include "kis_selection_mask.h" class KisSaveGroupVisitor : public KisNodeVisitor { public: KisSaveGroupVisitor(KisImageWSP image, bool saveInvisible, bool saveTopLevelOnly, const QUrl &url, const QString &baseName, const QString &extension, const QString &mimeFilter) : m_image(image) , m_saveInvisible(saveInvisible) , m_saveTopLevelOnly(saveTopLevelOnly) , m_url(url) , m_baseName(baseName) , m_extension(extension) , m_mimeFilter(mimeFilter) { } virtual ~KisSaveGroupVisitor() { } public: bool visit(KisNode* ) { return true; } bool visit(KisPaintLayer *) { return true; } bool visit(KisAdjustmentLayer *) { return true; } bool visit(KisExternalLayer *) { return true; } bool visit(KisCloneLayer *) { return true; } bool visit(KisFilterMask *) { return true; } bool visit(KisTransformMask *) { return true; } bool visit(KisTransparencyMask *) { return true; } bool visit(KisGeneratorLayer * ) { return true; } bool visit(KisSelectionMask* ) { return true; } bool visit(KisGroupLayer *layer) { if (layer == m_image->rootLayer()) { KisLayerSP child = dynamic_cast(layer->firstChild().data()); while (child) { child->accept(*this); child = dynamic_cast(child->nextSibling().data()); } } else if (layer->visible() || m_saveInvisible) { QRect r = m_image->bounds(); KisDocument *d = KisPart::instance()->createDocument(); d->prepareForImport(); KisImageWSP dst = new KisImage(d->createUndoStore(), r.width(), r.height(), m_image->colorSpace(), layer->name()); dst->setResolution(m_image->xRes(), m_image->yRes()); d->setCurrentImage(dst); KisPaintLayer* paintLayer = new KisPaintLayer(dst, "projection", layer->opacity()); KisPainter gc(paintLayer->paintDevice()); gc.bitBlt(QPoint(0, 0), layer->projection(), r); dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0)); dst->refreshGraph(); d->setOutputMimeType(m_mimeFilter.toLatin1()); d->setSaveInBatchMode(true); QUrl url = m_url; url = url.adjusted(QUrl::RemoveFilename); url.setPath(url.path() + m_baseName + '_' + layer->name().replace(' ', '_') + '.' + m_extension); d->exportDocument(url); if (!m_saveTopLevelOnly) { KisGroupLayerSP child = dynamic_cast(layer->firstChild().data()); while (child) { child->accept(*this); child = dynamic_cast(child->nextSibling().data()); } } delete d; } return true; } private: KisImageWSP m_image; bool m_saveInvisible; bool m_saveTopLevelOnly; QUrl m_url; QString m_baseName; QString m_extension; QString m_mimeFilter; }; KisLayerManager::KisLayerManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_imageFlatten(0) , m_imageMergeLayer(0) , m_groupLayersSave(0) , m_imageResizeToLayer(0) , m_flattenLayer(0) , m_rasterizeLayer(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) , m_layerStyle(0) { } KisLayerManager::~KisLayerManager() { delete m_commandsAdapter; } void KisLayerManager::setView(QPointerview) { m_imageView = view; } KisLayerSP KisLayerManager::activeLayer() { if (m_imageView) { return m_imageView->currentLayer(); } return 0; } KisPaintDeviceSP KisLayerManager::activeDevice() { if (activeLayer()) { return activeLayer()->paintDevice(); } return 0; } void KisLayerManager::activateLayer(KisLayerSP layer) { if (m_imageView) { emit sigLayerActivated(layer); layersUpdated(); if (layer) { m_view->resourceProvider()->slotNodeActivated(layer.data()); } } } void KisLayerManager::setup(KisActionManager* actionManager) { m_imageFlatten = new KisAction(i18n("&Flatten image"), this); m_imageFlatten->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("flatten_image", m_imageFlatten); m_imageFlatten->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_E)); connect(m_imageFlatten, SIGNAL(triggered()), this, SLOT(flattenImage())); m_imageMergeLayer = new KisAction(i18n("&Merge with Layer Below"), this); m_imageMergeLayer->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("merge_layer", m_imageMergeLayer); m_imageMergeLayer->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); connect(m_imageMergeLayer, SIGNAL(triggered()), this, SLOT(mergeLayer())); m_flattenLayer = new KisAction(i18n("&Flatten Layer"), this); m_flattenLayer->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("flatten_layer", m_flattenLayer); connect(m_flattenLayer, SIGNAL(triggered()), this, SLOT(flattenLayer())); KisAction * action = new KisAction(i18n("Rename current layer"), this); action->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("RenameCurrentLayer", action); action->setDefaultShortcut(QKeySequence(Qt::Key_F2)); connect(action, SIGNAL(triggered()), this, SLOT(layerProperties())); m_rasterizeLayer = new KisAction(i18n("Rasterize Layer"), this); m_rasterizeLayer->setActivationFlags(KisAction::ACTIVE_SHAPE_LAYER); m_rasterizeLayer->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("rasterize_layer", m_rasterizeLayer); connect(m_rasterizeLayer, SIGNAL(triggered()), this, SLOT(rasterizeLayer())); m_groupLayersSave = new KisAction(KisIconUtils::loadIcon("document-save"), i18n("Save Group Layers..."), this); m_groupLayersSave->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("save_groups_as_images", m_groupLayersSave); connect(m_groupLayersSave, SIGNAL(triggered()), this, SLOT(saveGroupLayers())); m_imageResizeToLayer = new KisAction(i18n("Trim to Current Layer"), this); m_imageResizeToLayer->setActivationFlags(KisAction::ACTIVE_LAYER); actionManager->addAction("resizeimagetolayer", m_imageResizeToLayer); connect(m_imageResizeToLayer, SIGNAL(triggered()), this, SLOT(imageResizeToActiveLayer())); KisAction *trimToImage = new KisAction(KisIconUtils::loadIcon("trim-to-image"), i18n("Trim to Image Size"), this); trimToImage->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager->addAction("trim_to_image", trimToImage); connect(trimToImage, SIGNAL(triggered()), this, SLOT(trimToImage())); m_layerStyle = new KisAction(i18n("Layer Style..."), this); m_layerStyle->setActivationFlags(KisAction::ACTIVE_LAYER); m_layerStyle->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("layer_style", m_layerStyle); connect(m_layerStyle, SIGNAL(triggered()), this, SLOT(layerStyle())); } void KisLayerManager::updateGUI() { KisImageWSP image = m_view->image(); KisLayerSP layer; qint32 nlayers = 0; if (image) { layer = activeLayer(); nlayers = image->nlayers(); } // XXX these should be named layer instead of image m_imageFlatten->setEnabled(nlayers > 1); m_imageMergeLayer->setEnabled(nlayers > 1 && layer && layer->prevSibling()); m_flattenLayer->setEnabled(nlayers > 1 && layer && layer->firstChild()); if (m_view->statusBar()) m_view->statusBar()->setProfile(image); } void KisLayerManager::imageResizeToActiveLayer() { KisLayerSP layer; KisImageWSP image = m_view->image(); if (image && (layer = activeLayer())) { QRect cropRect = layer->projection()->nonDefaultPixelArea(); if (!cropRect.isEmpty()) { image->cropImage(cropRect); } else { m_view->showFloatingMessage( i18nc("floating message in layer manager", "Layer is empty "), QIcon(), 2000, KisFloatingMessage::Low); } } } void KisLayerManager::trimToImage() { KisImageWSP image = m_view->image(); if (image) { image->cropImage(image->bounds()); } } void KisLayerManager::layerProperties() { if (!m_view) return; if (!m_view->document()) return; KisLayerSP layer = activeLayer(); if (!layer) return; if (KisAdjustmentLayerSP alayer = KisAdjustmentLayerSP(dynamic_cast(layer.data()))) { KisPaintDeviceSP dev = alayer->projection(); KisDlgAdjLayerProps dlg(alayer, alayer.data(), dev, m_view, alayer->filter().data(), alayer->name(), i18n("Filter Layer Properties"), m_view->mainWindow(), "dlgadjlayerprops"); dlg.resize(dlg.minimumSizeHint()); KisSafeFilterConfigurationSP configBefore(alayer->filter()); KIS_ASSERT_RECOVER_RETURN(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { alayer->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); alayer->setDirty(); } } } else if (KisGeneratorLayerSP alayer = KisGeneratorLayerSP(dynamic_cast(layer.data()))) { KisDlgGeneratorLayer dlg(alayer->name(), m_view, m_view->mainWindow()); dlg.setCaption(i18n("Fill Layer Properties")); KisSafeFilterConfigurationSP configBefore(alayer->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); dlg.setConfiguration(configBefore.data()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { alayer->setName(dlg.layerName()); KisSafeFilterConfigurationSP configAfter(dlg.configuration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(alayer, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, true); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } } else { // If layer == normal painting layer, vector layer, or group layer KisDlgLayerProperties *dialog = new KisDlgLayerProperties(layer, m_view, m_view->document()); dialog->resize(dialog->minimumSizeHint()); dialog->setAttribute(Qt::WA_DeleteOnClose); Qt::WindowFlags flags = dialog->windowFlags(); dialog->setWindowFlags(flags | Qt::WindowStaysOnTopHint | Qt::Dialog); dialog->show(); } } void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source) { KisImageWSP image = m_view->image(); if (!image) return; KisPaintDeviceSP srcDevice = source->paintDevice() ? source->projection() : source->original(); if (!srcDevice) return; KisPaintDeviceSP clone; if (!(*srcDevice->colorSpace() == *srcDevice->compositionSourceColorSpace())) { clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace()); QRect rc(srcDevice->extent()); KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc); } else { clone = new KisPaintDevice(*srcDevice); } KisLayerSP layer = new KisPaintLayer(image, source->name(), source->opacity(), clone); layer->setCompositeOp(source->compositeOpId()); KisNodeSP parent = source->parent(); KisNodeSP above = source; while (parent && !parent->allowAsChild(layer)) { above = above->parent(); parent = above ? above->parent() : 0; } m_commandsAdapter->beginMacro(kundo2_i18n("Convert to a Paint Layer")); m_commandsAdapter->addNode(layer, parent, above); m_commandsAdapter->removeNode(source); m_commandsAdapter->endMacro(); } void KisLayerManager::adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(activeNode); parent = activeNode; above = parent->lastChild(); while (parent && (!parent->allowAsChild(node) || parent->userLocked())) { above = parent; parent = parent->parent(); } if (!parent) { warnKrita << "KisLayerManager::adjustLayerPosition:" << "No node accepted newly created node"; parent = m_view->image()->root(); above = parent->lastChild(); } } void KisLayerManager::addLayerCommon(KisNodeSP activeNode, KisLayerSP layer) { KisNodeSP parent; KisNodeSP above; adjustLayerPosition(layer, activeNode, parent, above); m_commandsAdapter->addNode(layer, parent, above); } void KisLayerManager::addLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace())); } void KisLayerManager::addGroupLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisGroupLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addCloneLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); addLayerCommon(activeNode, new KisCloneLayer(activeLayer(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8)); } void KisLayerManager::addShapeLayer(KisNodeSP activeNode) { if (!m_view) return; if (!m_view->document()) return; KisImageWSP image = m_view->image(); KisShapeLayerSP layer = new KisShapeLayer(m_view->document()->shapeController(), image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8); addLayerCommon(activeNode, layer); } void KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisSelectionSP selection = m_view->selection(); KisAdjustmentLayerSP adjl = addAdjustmentLayer(activeNode, QString(), 0, selection); image->refreshGraph(); KisPaintDeviceSP previewDevice = new KisPaintDevice(*adjl->original()); KisDlgAdjustmentLayer dlg(adjl, adjl.data(), previewDevice, image->nextLayerName(), i18n("New Filter Layer"), m_view); dlg.resize(dlg.minimumSizeHint()); // ensure that the device may be free'd by the dialog // when it is not needed anymore previewDevice = 0; if (dlg.exec() != QDialog::Accepted || adjl->filter().isNull()) { // XXX: add messagebox warning if there's no filter set! m_commandsAdapter->undoLastCommand(); } else { adjl->setName(dlg.layerName()); } } KisAdjustmentLayerSP KisLayerManager::addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfiguration * filter, KisSelectionSP selection) { KisImageWSP image = m_view->image(); KisAdjustmentLayerSP layer = new KisAdjustmentLayer(image, name, filter, selection); addLayerCommon(activeNode, layer); return layer; } void KisLayerManager::addGeneratorLayer(KisNodeSP activeNode) { KisImageWSP image = m_view->image(); KisDlgGeneratorLayer dlg(image->nextLayerName(), m_view, m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { KisSelectionSP selection = m_view->selection(); KisFilterConfiguration * generator = dlg.configuration(); QString name = dlg.layerName(); addLayerCommon(activeNode, new KisGeneratorLayer(image, name, generator, selection)); } } void KisLayerManager::layerDuplicate() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP active = activeLayer(); if (!active) return; KisLayerSP dup = dynamic_cast(active->clone().data()); m_commandsAdapter->addNode(dup.data(), active->parent(), active.data()); if (dup) { activateLayer(dup); } else { QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("Could not add layer to image.")); } } void KisLayerManager::layerRaise() { KisImageWSP image = m_view->image(); KisLayerSP layer; if (!image) return; layer = activeLayer(); m_commandsAdapter->raise(layer); layer->parent()->setDirty(); } void KisLayerManager::layerLower() { KisImageWSP image = m_view->image(); KisLayerSP layer; if (!image) return; layer = activeLayer(); m_commandsAdapter->lower(layer); layer->parent()->setDirty(); } void KisLayerManager::layerFront() { KisImageWSP image = m_view->image(); KisLayerSP layer; if (!image) return; layer = activeLayer(); m_commandsAdapter->toTop(layer); layer->parent()->setDirty(); } void KisLayerManager::layerBack() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer; layer = activeLayer(); m_commandsAdapter->toBottom(layer); layer->parent()->setDirty(); } void KisLayerManager::rotateLayer(double radians) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->rotateNode(layer, radians); } void KisLayerManager::shearLayer(double angleX, double angleY) { if (!m_view->image()) return; KisLayerSP layer = activeLayer(); if (!layer) return; m_view->image()->shearNode(layer, angleX, angleY); } void KisLayerManager::flattenImage() { KisImageWSP image = m_view->image(); if (image) { bool doIt = true; if (image->nHiddenLayers() > 0) { int answer = QMessageBox::warning(m_view->mainWindow(), i18nc("@title:window", "Flatten Image"), i18n("The image contains hidden layers that will be lost. Do you want to flatten the image?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer != QMessageBox::Yes) { doIt = false; } } if (doIt) { image->flatten(); } } } inline bool isSelectionMask(KisNodeSP node) { return dynamic_cast(node.data()); } bool tryMergeSelectionMasks(KisNodeSP currentNode, KisImageSP image) { bool result = false; KisNodeSP prevNode = currentNode->prevSibling(); if (isSelectionMask(currentNode) && prevNode && isSelectionMask(prevNode)) { QList mergedNodes; mergedNodes.append(currentNode); mergedNodes.append(prevNode); image->mergeMultipleLayers(mergedNodes, currentNode); result = true; } return result; } void KisLayerManager::mergeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, layer); } else if (!tryMergeSelectionMasks(m_view->activeNode(), image)) { if (!layer->prevSibling()) return; KisLayer *prevLayer = dynamic_cast(layer->prevSibling().data()); if (!prevLayer) return; if (layer->metaData()->isEmpty() && prevLayer->metaData()->isEmpty()) { image->mergeDown(layer, KisMetaData::MergeStrategyRegistry::instance()->get("Drop")); } else { const KisMetaData::MergeStrategy* strategy = KisMetaDataMergeStrategyChooserWidget::showDialog(m_view->mainWindow()); if (!strategy) return; image->mergeDown(layer, strategy); } } m_view->updateGUI(); } void KisLayerManager::flattenLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisLayerSP newLayer = image->flattenLayer(layer); if (newLayer) { newLayer->setDirty(); } m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); gc.bitBlt(rc.topLeft(), layer->projection(), rc); m_commandsAdapter->beginMacro(kundo2_i18n("Rasterize Layer")); m_commandsAdapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); int childCount = layer->childCount(); for (int i = 0; i < childCount; i++) { m_commandsAdapter->moveNode(layer->firstChild(), paintLayer, paintLayer->lastChild()); } m_commandsAdapter->removeNode(layer); m_commandsAdapter->endMacro(); updateGUI(); } void KisLayerManager::layersUpdated() { KisLayerSP layer = activeLayer(); if (!layer) return; m_view->updateGUI(); } void KisLayerManager::saveGroupLayers() { QStringList listMimeFilter = KisImportExportManager::mimeFilter("application/x-krita", KisImportExportManager::Export); KoDialog dlg; QWidget *page = new QWidget(&dlg); dlg.setMainWidget(page); QBoxLayout *layout = new QVBoxLayout(page); KFileWidget *fd = new KFileWidget(QUrl::fromLocalFile(m_view->document()->url().path()), page); fd->setUrl(m_view->document()->url()); fd->setMimeFilter(listMimeFilter); fd->setOperationMode(KFileWidget::Saving); layout->addWidget(fd); QCheckBox *chkInvisible = new QCheckBox(i18n("Convert Invisible Groups"), page); chkInvisible->setChecked(false); layout->addWidget(chkInvisible); QCheckBox *chkDepth = new QCheckBox(i18n("Export Only Toplevel Groups"), page); chkDepth->setChecked(true); layout->addWidget(chkDepth); if (!dlg.exec()) return; // selectedUrl()( does not return the expected result. So, build up the QUrl the more complicated way //return m_fileWidget->selectedUrl(); QUrl url = fd->dirOperator()->url(); QString path = fd->locationEdit()->currentText(); QFileInfo f(path); QString extension = f.completeSuffix(); QString basename = f.baseName(); QString mimefilter = fd->currentMimeFilter(); if (mimefilter.isEmpty()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForUrl(url); mimefilter = mime.name(); extension = db.suffixForFileName(path); } if (url.isEmpty()) return; KisImageWSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), url, basename, extension, mimefilter); image->rootLayer()->accept(v); } bool KisLayerManager::activeLayerHasSelection() { return (activeLayer()->selection() != 0); } void KisLayerManager::addFileLayer(KisNodeSP activeNode) { QString basePath; QUrl url = m_view->document()->url(); if (url.isLocalFile()) { basePath = QFileInfo(url.toLocalFile()).absolutePath(); } KisImageWSP image = m_view->image(); KisDlgFileLayer dlg(basePath, image->nextLayerName(), m_view->mainWindow()); dlg.resize(dlg.minimumSizeHint()); if (dlg.exec() == QDialog::Accepted) { QString name = dlg.layerName(); QString fileName = dlg.fileName(); if(fileName.isEmpty()){ QMessageBox::critical(m_view->mainWindow(), i18nc("@title:window", "Krita"), i18n("No file name specified")); return; } KisFileLayer::ScalingMethod scalingMethod = dlg.scaleToImageResolution(); addLayerCommon(activeNode, new KisFileLayer(image, basePath, fileName, scalingMethod, name, OPACITY_OPAQUE_U8)); } } void updateLayerStyles(KisLayerSP layer, KisDlgLayerStyle *dlg) { KisSetLayerStyleCommand::updateLayerStyle(layer, dlg->style()->clone()); } void KisLayerManager::layerStyle() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); } else { oldStyle = toQShared(new KisPSDLayerStyle()); } KisDlgLayerStyle dlg(oldStyle->clone(), m_view->resourceProvider()); boost::function updateCall(boost::bind(updateLayerStyles, layer, &dlg)); SignalToFunctionProxy proxy(updateCall); connect(&dlg, SIGNAL(configChanged()), &proxy, SLOT(start())); if (dlg.exec() == QDialog::Accepted) { KisPSDLayerStyleSP newStyle = dlg.style(); KUndo2CommandSP command = toQShared( new KisSetLayerStyleCommand(layer, oldStyle, newStyle)); image->postExecutionUndoAdapter()->addCommand(command); } } diff --git a/krita/ui/kis_layer_manager.h b/krita/ui/kis_layer_manager.h index 45aa7db4bf..37bbeb074d 100644 --- a/krita/ui/kis_layer_manager.h +++ b/krita/ui/kis_layer_manager.h @@ -1,140 +1,139 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_LAYER_MANAGER #define KIS_LAYER_MANAGER #include #include #include #include "kis_adjustment_layer.h" #include "kis_types.h" #include "KisView.h" -class QAction; class KisViewManager; class KisFilterConfiguration; class KisNodeCommandsAdapter; class KisAction; class KisActionManager; /** * KisLayerManager takes care of the gui around working with layers: * adding, removing, editing. It also keeps track of the active layer * for this view. */ class KisLayerManager : public QObject { Q_OBJECT public: KisLayerManager(KisViewManager * view); ~KisLayerManager(); void setView(QPointerview); Q_SIGNALS: void sigLayerActivated(KisLayerSP layer); private: friend class KisNodeManager; /** * Activate the specified layer. The layer may be 0. */ void activateLayer(KisLayerSP layer); KisLayerSP activeLayer(); KisPaintDeviceSP activeDevice(); void setup(KisActionManager *actionManager); void updateGUI(); void rotateLayer(double radians); void shearLayer(double angleX, double angleY); private Q_SLOTS: void mergeLayer(); void imageResizeToActiveLayer(); void trimToImage(); void layerProperties(); void layerDuplicate(); void layerRaise(); void layerLower(); void layerFront(); void layerBack(); void flattenImage(); void flattenLayer(); void rasterizeLayer(); void layersUpdated(); void saveGroupLayers(); bool activeLayerHasSelection(); void convertNodeToPaintLayer(KisNodeSP source); void addLayer(KisNodeSP activeNode); void addGroupLayer(KisNodeSP activeNode); void addCloneLayer(KisNodeSP activeNode); void addShapeLayer(KisNodeSP activeNode); void addAdjustmentLayer(KisNodeSP activeNode); KisAdjustmentLayerSP addAdjustmentLayer(KisNodeSP activeNode, const QString & name, KisFilterConfiguration * filter, KisSelectionSP selection); void addGeneratorLayer(KisNodeSP activeNode); void addFileLayer(KisNodeSP activeNode); void layerStyle(); private: void adjustLayerPosition(KisNodeSP node, KisNodeSP activeNode, KisNodeSP &parent, KisNodeSP &above); void addLayerCommon(KisNodeSP activeNode, KisLayerSP layer); private: KisViewManager * m_view; QPointerm_imageView; KisAction *m_imageFlatten; KisAction *m_imageMergeLayer; KisAction *m_groupLayersSave; KisAction *m_imageResizeToLayer; KisAction *m_flattenLayer; KisAction *m_rasterizeLayer; KisNodeCommandsAdapter* m_commandsAdapter; KisAction *m_layerStyle; }; #endif diff --git a/krita/ui/kis_mask_manager.cc b/krita/ui/kis_mask_manager.cc index 6243663f91..c8282e771d 100644 --- a/krita/ui/kis_mask_manager.cc +++ b/krita/ui/kis_mask_manager.cc @@ -1,361 +1,360 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_mask_manager.h" #include #include #include #include #include #include #include #include "KisDocument.h" #include "KisViewManager.h" #include #include #include #include #include #include #include #include #include "dialogs/kis_dlg_adjustment_layer.h" #include "widgets/kis_mask_widgets.h" #include #include #include #include "dialogs/kis_dlg_adj_layer_props.h" #include #include #include #include #include "kis_node_commands_adapter.h" #include "commands/kis_selection_commands.h" #include "kis_iterator_ng.h" -#include "KisView.h" KisMaskManager::KisMaskManager(KisViewManager * view) : m_view(view) , m_imageView(0) , m_commandsAdapter(new KisNodeCommandsAdapter(m_view)) { } void KisMaskManager::setView(QPointerimageView) { m_imageView = imageView; } void KisMaskManager::setup(KActionCollection *actionCollection, KisActionManager *actionManager) { Q_UNUSED(actionCollection); Q_UNUSED(actionManager); } void KisMaskManager::updateGUI() { // XXX: enable/disable menu items according to whether there's a mask selected currently // XXX: disable the selection mask item if there's already a selection mask // YYY: doesn't KisAction do that already? } KisMaskSP KisMaskManager::activeMask() { if (m_imageView) { return m_imageView->currentMask(); } return 0; } KisPaintDeviceSP KisMaskManager::activeDevice() { // XXX: we may also need to have a possibility of getting the vector // part of selection here if (m_imageView) { KisSelectionSP selection; KisMaskSP mask = m_imageView->currentMask(); return mask && (selection = mask->selection()) ? selection->pixelSelection() : 0; } return 0; } void KisMaskManager::activateMask(KisMaskSP mask) { Q_UNUSED(mask); } void KisMaskManager::masksUpdated() { m_view->updateGUI(); } void KisMaskManager::adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above) { Q_ASSERT(node); Q_ASSERT(activeNode); if (!avoidActiveNode && activeNode->allowAsChild(node)) { parent = activeNode; above = activeNode->lastChild(); } else if (activeNode->parent() && activeNode->parent()->allowAsChild(node)) { parent = activeNode->parent(); above = activeNode; } else { KisNodeSP t = activeNode; while ((t = t->nextSibling())) { if (t->allowAsChild(node)) { parent = t; above = t->lastChild(); break; } } if (!t) { t = activeNode; while ((t = t->prevSibling())) { if (t->allowAsChild(node)) { parent = t; above = t->lastChild(); break; } } } if (!t && activeNode->parent()) { adjustMaskPosition(node, activeNode->parent(), true, parent, above); } else if (!t) { KisImageWSP image = m_view->image(); KisLayerSP layer = new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, image->colorSpace()); m_commandsAdapter->addNode(layer, activeNode, 0); parent = layer; above = 0; } } } void KisMaskManager::createMaskCommon(KisMaskSP mask, KisNodeSP activeNode, KisPaintDeviceSP copyFrom, const KUndo2MagicString& macroName, const QString &nodeType, const QString &nodeName, bool suppressSelection, bool avoidActiveNode) { m_commandsAdapter->beginMacro(macroName); KisNodeSP parent; KisNodeSP above; adjustMaskPosition(mask, activeNode, avoidActiveNode, parent, above); KisLayerSP parentLayer = dynamic_cast(parent.data()); Q_ASSERT(parentLayer); if (!suppressSelection) { if (copyFrom) { mask->initSelection(copyFrom, parentLayer); } else { mask->initSelection(m_view->selection(), parentLayer); } } //counting number of KisSelectionMask QList masks = parentLayer->childNodes(QStringList(nodeType),KoProperties()); int number = masks.count() + 1; mask->setName(nodeName + QString(" ") + QString::number(number)); m_commandsAdapter->addNode(mask, parentLayer, above); m_commandsAdapter->endMacro(); masksUpdated(); } void KisMaskManager::createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool avoidActiveNode) { KisSelectionMaskSP mask = new KisSelectionMask(m_view->image()); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Selection Mask"), "KisSelectionMask", i18n("Selection"), false, avoidActiveNode); mask->setActive(true); } void KisMaskManager::createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool avoidActiveNode) { KisMaskSP mask = new KisTransparencyMask(); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Transparency Mask"), "KisTransparencyMask", i18n("Transparency Mask"), false, avoidActiveNode); } void KisMaskManager::createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool avoidActiveNode) { KisFilterMaskSP mask = new KisFilterMask(); createMaskCommon(mask, activeNode, copyFrom, kundo2_i18n("Add Filter Mask"), "KisFilterMask", i18n("Filter Mask"), false, avoidActiveNode); /** * FIXME: We'll use layer's original for creation of a thumbnail. * Actually, we can't use it's projection as newly created mask * may be going to be inserted in the middle of the masks stack */ KisPaintDeviceSP originalDevice = mask->parent()->original(); KisDlgAdjustmentLayer dialog(mask, mask.data(), originalDevice, mask->name(), i18n("New Filter Mask"), m_view); // If we are supposed to not disturb the user, don't start asking them about things. if(quiet) { KisFilterConfiguration *filter = KisFilterRegistry::instance()->values().first()->defaultConfiguration(originalDevice); if (filter) { mask->setFilter(filter); mask->setName(mask->name()); } return; } if (dialog.exec() == QDialog::Accepted) { KisFilterConfiguration *filter = dialog.filterConfiguration(); if (filter) { QString name = dialog.layerName(); mask->setFilter(filter); mask->setName(name); } } else { m_commandsAdapter->undoLastCommand(); } } void KisMaskManager::createTransformMask(KisNodeSP activeNode) { KisTransformMaskSP mask = new KisTransformMask(); createMaskCommon(mask, activeNode, 0, kundo2_i18n("Add Transform Mask"), "KisTransformMask", i18n("Transform Mask"), true, false); } void KisMaskManager::duplicateMask() { if (!activeMask()) return; if (!m_view->image()) return; // If it's a global mask we shouldn't be able to duplicate // TODO: Maybe add this check to KisSelectionMask function to know if it's global or not? if(activeMask()->parent().data() == m_view->image()->rootLayer().data()) return; KisMaskSP newMask = dynamic_cast(activeMask()->clone().data()); newMask->setName(i18n("Duplication of ") + activeMask()->name()); m_commandsAdapter->addNode(newMask, activeMask()->parent(), activeMask()); KisSelectionMaskSP selectionMask = dynamic_cast(newMask.data()); if (selectionMask) { selectionMask->setActive(true); } masksUpdated(); } void KisMaskManager::removeMask() { if (!activeMask()) return; if (!m_view->image()) return; m_commandsAdapter->removeNode(activeMask()); masksUpdated(); } void KisMaskManager::maskProperties() { if (!activeMask()) return; if (activeMask()->inherits("KisFilterMask")) { KisFilterMask *mask = static_cast(activeMask().data()); KisLayerSP layer = dynamic_cast(mask->parent().data()); if (! layer) return; KisPaintDeviceSP dev = layer->original(); if (!dev) { return; } KisDlgAdjLayerProps dlg(layer, mask, dev, m_view, mask->filter().data(), mask->name(), i18n("Filter Mask Properties"), m_view->mainWindow(), "dlgeffectmaskprops"); KisSafeFilterConfigurationSP configBefore(mask->filter()); Q_ASSERT(configBefore); QString xmlBefore = configBefore->toXML(); if (dlg.exec() == QDialog::Accepted) { KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); mask->setName(dlg.layerName()); if(xmlBefore != xmlAfter) { KisChangeFilterCmd *cmd = new KisChangeFilterCmd(mask, configBefore->name(), xmlBefore, configAfter->name(), xmlAfter, false); // FIXME: check whether is needed cmd->redo(); m_view->undoAdapter()->addCommand(cmd); m_view->document()->setModified(true); } } else { KisSafeFilterConfigurationSP configAfter(dlg.filterConfiguration()); Q_ASSERT(configAfter); QString xmlAfter = configAfter->toXML(); if(xmlBefore != xmlAfter) { mask->setFilter(KisFilterRegistry::instance()->cloneConfiguration(configBefore.data())); mask->setDirty(); } } } else { // Not much to show for transparency or selection masks? } } void KisMaskManager::raiseMask() { if (!activeMask()) return; if (!m_view->image()) return; m_commandsAdapter->raise(activeMask()); } void KisMaskManager::lowerMask() { if (!activeMask()) return; if (!m_view->image()) return; m_commandsAdapter->lower(activeMask()); } void KisMaskManager::maskToTop() { if (!activeMask()) return; if (!m_view->image()) return; m_commandsAdapter->toTop(activeMask()); } void KisMaskManager::maskToBottom() { if (!activeMask()) return; if (!m_view->image()) return; m_commandsAdapter->toBottom(activeMask()); } diff --git a/krita/ui/kis_mask_manager.h b/krita/ui/kis_mask_manager.h index f1506695d0..de7e34ee53 100644 --- a/krita/ui/kis_mask_manager.h +++ b/krita/ui/kis_mask_manager.h @@ -1,136 +1,134 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_MASK_MANAGER #define KIS_MASK_MANAGER #include #include #include "kis_types.h" #include "KisView.h" class KisViewManager; class KActionCollection; -class KisAction; -class QAction; class KisNodeCommandsAdapter; class KisActionManager; #include "kis_mask.h" /** * Handle the gui for manipulating masks. */ class KisMaskManager : public QObject { Q_OBJECT public: KisMaskManager(KisViewManager * view); ~KisMaskManager() {} void setView(QPointerview); private: friend class KisNodeManager; void setup(KActionCollection * actionCollection, KisActionManager *actionManager); void updateGUI(); /** * @return the paint device associated with the currently * active mask, if there is one. */ KisPaintDeviceSP activeDevice(); /** * @return the active mask, if there is one */ KisMaskSP activeMask(); /** * Create an exact duplicate of the current mask. */ void duplicateMask(); /** * Delete the mask */ void removeMask(); /** * Move the mask one up in the mask stack. If we reach the top * of the stack, try to move the mask to the layer on top of the * active layer. */ void raiseMask(); /** * Move the mask one down in the mask stack. If we reach the bottom * of the stack, try to move the mask to the layer beneath the * active layer. */ void lowerMask(); /** * Move the mask to the top of the mask stack of the active layer */ void maskToTop(); /** * Move the mask to the bottom of the mask stack of the active * layer */ void maskToBottom(); /** * Show the mask properties dialog */ void maskProperties(); /** * called whenever the mask stack is updated to enable/disable all * menu items */ void masksUpdated(); /** * Activate a new mask. There can be only one mask active per * view; and if the mask is active, it becomes the paint device. */ void activateMask(KisMaskSP mask); void adjustMaskPosition(KisNodeSP node, KisNodeSP activeNode, bool avoidActiveNode, KisNodeSP &parent, KisNodeSP &above); void createMaskCommon(KisMaskSP mask, KisNodeSP activeNode, KisPaintDeviceSP copyFrom, const KUndo2MagicString ¯oName, const QString &nodeType, const QString &nodeName, bool suppressSelection, bool avoidActiveNode); void createSelectionMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool avoidActiveNode); void createFilterMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool quiet, bool avoidActiveNode); void createTransformMask(KisNodeSP activeNode); void createTransparencyMask(KisNodeSP activeNode, KisPaintDeviceSP copyFrom, bool avoidActiveNode); KisViewManager * m_view; QPointerm_imageView; KisNodeCommandsAdapter* m_commandsAdapter; }; #endif // KIS_MASK_MANAGER diff --git a/krita/ui/kis_mirror_manager.cpp b/krita/ui/kis_mirror_manager.cpp index 4afd8327ae..f5524f3c53 100644 --- a/krita/ui/kis_mirror_manager.cpp +++ b/krita/ui/kis_mirror_manager.cpp @@ -1,89 +1,88 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_mirror_manager.h" -#include "KisView.h" #include "KisViewManager.h" #include #include #include #include #include #include #include "canvas/kis_mirror_axis.h" KisMirrorManager::KisMirrorManager(KisViewManager* view) : QObject(view) , m_imageView(0) { } KisMirrorManager::~KisMirrorManager() { } void KisMirrorManager::setup(KActionCollection * collection) { m_mirrorCanvas = new KToggleAction(i18n("Mirror View"), this); m_mirrorCanvas->setChecked(false); //m_mirrorCanvas->setShortcut(QKeySequence(Qt::Key_M)); collection->addAction("mirror_canvas", m_mirrorCanvas); collection->setDefaultShortcut(m_mirrorCanvas, QKeySequence(Qt::Key_M)); updateAction(); } void KisMirrorManager::setView(QPointer imageView) { if (m_imageView) { m_mirrorCanvas->disconnect(); } m_imageView = imageView; if (m_imageView && !decoration()) { m_imageView->canvasBase()->addDecoration(new KisMirrorAxis(m_imageView->viewManager()->resourceProvider(), m_imageView)); } if (m_imageView && decoration()) { connect(m_mirrorCanvas, SIGNAL(toggled(bool)), dynamic_cast(m_imageView->canvasController()), SLOT(mirrorCanvas(bool))); } updateAction(); } void KisMirrorManager::updateAction() { if (decoration()) { m_mirrorCanvas->setChecked(decoration()->visible()); m_mirrorCanvas->setEnabled(true); } else { m_mirrorCanvas->setEnabled(false); } } KisMirrorAxis* KisMirrorManager::decoration() { if (m_imageView && m_imageView->canvasBase()) { return dynamic_cast(m_imageView->canvasBase()->decoration("mirror_axis")); } return 0; } diff --git a/krita/ui/kis_painting_assistant.h b/krita/ui/kis_painting_assistant.h index ab5077967a..d52556b47b 100644 --- a/krita/ui/kis_painting_assistant.h +++ b/krita/ui/kis_painting_assistant.h @@ -1,175 +1,174 @@ /* * Copyright (c) 2008 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_PAINTING_ASSISTANT_H_ #define _KIS_PAINTING_ASSISTANT_H_ #include #include #include #include #include #include #include #include #include class QPainter; class QRect; class QRectF; class KoStore; class KisCoordinatesConverter; -class KisDocument; #include #include class KisPaintingAssistantHandle; typedef KisSharedPtr KisPaintingAssistantHandleSP; class KisPaintingAssistant; class QPainterPath; /** * Represent an handle of the assistant, used to edit the parameters * of an assistants. Handles can be shared between assistants. */ class KRITAUI_EXPORT KisPaintingAssistantHandle : public QPointF, public KisShared { friend class KisPaintingAssistant; public: KisPaintingAssistantHandle(double x, double y); explicit KisPaintingAssistantHandle(QPointF p); KisPaintingAssistantHandle(const KisPaintingAssistantHandle&); ~KisPaintingAssistantHandle(); void mergeWith(KisPaintingAssistantHandleSP); QList split(); void uncache(); KisPaintingAssistantHandle& operator=(const QPointF&); void setType(char type); char handleType(); private: void registerAssistant(KisPaintingAssistant*); void unregisterAssistant(KisPaintingAssistant*); bool containsAssistant(KisPaintingAssistant*); private: struct Private; Private* const d; }; /** * A KisPaintingAssistant is an object that assist the drawing on the canvas. * With this class you can implement virtual equivalent to ruler or compas. */ class KRITAUI_EXPORT KisPaintingAssistant { public: KisPaintingAssistant(const QString& id, const QString& name); virtual ~KisPaintingAssistant(); const QString& id() const; const QString& name() const; bool snapping() const;//this returns whether or not the snapping is/should be active. void setSnapping(bool set); bool outline() const;//this returns whether or not the preview is/should be active. void setOutline(bool set); /** * Adjust the position given in parameter. * @param point the coordinates in point in the document reference * @param strokeBegin the coordinates of the beginning of the stroke */ virtual QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin) = 0; virtual void endStroke() { } virtual QPointF buttonPosition() const = 0; virtual int numHandles() const = 0; void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with); void addHandle(KisPaintingAssistantHandleSP handle); void addSideHandle(KisPaintingAssistantHandleSP handle); virtual void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter, bool cached = true,KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true); void uncache(); const QList& handles() const; QList handles(); const QList& sideHandles() const; QList sideHandles(); QByteArray saveXml( QMap &handleMap); void loadXml(KoStore *store, QMap &handleMap, QString path); void saveXmlList(QDomDocument& doc, QDomElement& ssistantsElement, int count); void findHandleLocation(); KisPaintingAssistantHandleSP oppHandleOne(); /** * Get the topLeft, bottomLeft, topRight and BottomRight corners of the assistant */ const KisPaintingAssistantHandleSP topLeft() const; KisPaintingAssistantHandleSP topLeft(); const KisPaintingAssistantHandleSP topRight() const; KisPaintingAssistantHandleSP topRight(); const KisPaintingAssistantHandleSP bottomLeft() const; KisPaintingAssistantHandleSP bottomLeft(); const KisPaintingAssistantHandleSP bottomRight() const; KisPaintingAssistantHandleSP bottomRight(); const KisPaintingAssistantHandleSP topMiddle() const; KisPaintingAssistantHandleSP topMiddle(); const KisPaintingAssistantHandleSP rightMiddle() const; KisPaintingAssistantHandleSP rightMiddle(); const KisPaintingAssistantHandleSP leftMiddle() const; KisPaintingAssistantHandleSP leftMiddle(); const KisPaintingAssistantHandleSP bottomMiddle() const; KisPaintingAssistantHandleSP bottomMiddle(); public: /** * This will paint a path using a white and black colors. */ static void drawPath(QPainter& painter, const QPainterPath& path, bool drawActive=true); static void drawPreview(QPainter& painter, const QPainterPath& path); protected: virtual QRect boundingRect() const; virtual void drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) = 0; void initHandles(QList _handles); QList m_handles; private: struct Private; Private* const d; }; /** * Allow to create a painting assistant. */ class KRITAUI_EXPORT KisPaintingAssistantFactory { public: KisPaintingAssistantFactory(); virtual ~KisPaintingAssistantFactory(); virtual QString id() const = 0; virtual QString name() const = 0; virtual KisPaintingAssistant* createPaintingAssistant() const = 0; }; class KRITAUI_EXPORT KisPaintingAssistantFactoryRegistry : public KoGenericRegistry { public: KisPaintingAssistantFactoryRegistry(); ~KisPaintingAssistantFactoryRegistry(); static KisPaintingAssistantFactoryRegistry* instance(); }; #endif diff --git a/krita/ui/kis_painting_assistants_decoration.cpp b/krita/ui/kis_painting_assistants_decoration.cpp index 7f3987d08c..325409de59 100644 --- a/krita/ui/kis_painting_assistants_decoration.cpp +++ b/krita/ui/kis_painting_assistants_decoration.cpp @@ -1,215 +1,214 @@ /* * Copyright (c) 2009 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painting_assistants_decoration.h" #include #include #include #include #include #include #include "kis_debug.h" -#include "kis_painting_assistant.h" #include struct KisPaintingAssistantsDecoration::Private { QList assistants; bool assistantVisible; bool outlineVisible; bool snapOnlyOneAssistant; KisPaintingAssistant* firstAssistant; bool aFirstStroke; }; KisPaintingAssistantsDecoration::KisPaintingAssistantsDecoration(QPointer parent) : KisCanvasDecoration("paintingAssistantsDecoration", parent), d(new Private) { setAssistantVisible(true); setOutlineVisible(true); d->snapOnlyOneAssistant=true;//turn on by default. } KisPaintingAssistantsDecoration::~KisPaintingAssistantsDecoration() { qDeleteAll(d->assistants.begin(), d->assistants.end()); delete d; } void KisPaintingAssistantsDecoration::addAssistant(KisPaintingAssistant* assistant) { if (d->assistants.contains(assistant)) return; d->assistants.push_back(assistant); emit assistantChanged(); } void KisPaintingAssistantsDecoration::removeAssistant(KisPaintingAssistant* assistant) { delete assistant; d->assistants.removeAll(assistant); emit assistantChanged(); } void KisPaintingAssistantsDecoration::removeAll() { foreach (KisPaintingAssistant* assistant, d->assistants) { delete assistant; } d->assistants.clear(); emit assistantChanged(); } QPointF KisPaintingAssistantsDecoration::adjustPosition(const QPointF& point, const QPointF& strokeBegin) { if (d->assistants.empty()) return point; if (d->assistants.count() == 1) { if(d->assistants.first()->snapping()==true){ QPointF newpoint = d->assistants.first()->adjustPosition(point, strokeBegin); // check for NaN if (newpoint.x() != newpoint.x()) return point; return newpoint; } } QPointF best = point; double distance = DBL_MAX; //the following tries to find the closest point to stroke-begin. It checks all assistants for the closest point// if(!d->snapOnlyOneAssistant){ foreach(KisPaintingAssistant* assistant, d->assistants) { if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; } } } } else if (d->aFirstStroke==false) { foreach(KisPaintingAssistant* assistant, d->assistants) { if(assistant->snapping()==true){//this checks if the assistant in question has it's snapping boolean turned on// QPointF pt = assistant->adjustPosition(point, strokeBegin); if (pt.x() != pt.x()) continue; double dist = qAbs(pt.x() - point.x()) + qAbs(pt.y() - point.y()); if (dist < distance) { best = pt; distance = dist; d->firstAssistant = assistant; } } } } else if(d->firstAssistant) { //make sure there's a first assistant to begin with.// best = d->firstAssistant->adjustPosition(point, strokeBegin); } else { d->aFirstStroke=false; } //this is here to be compatible with the movement in the perspective tool. qreal dx = point.x() - strokeBegin.x(), dy = point.y() - strokeBegin.y(); if (dx * dx + dy * dy >= 4.0) { // allow some movement before snapping d->aFirstStroke=true; } return best; } void KisPaintingAssistantsDecoration::endStroke() { d->aFirstStroke=false; foreach(KisPaintingAssistant* assistant, d->assistants) { assistant->endStroke(); } } void KisPaintingAssistantsDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter,KisCanvas2* canvas) { if (!canvas) { dbgFile<<"canvas does not exist in painting assistant decoration, you may have passed arguments incorrectly:"<assistants) { assistant->drawAssistant(gc, updateRect, converter, true, canvas, assistantVisibility(), outlineVisibility()); } } //drawPreview// QList KisPaintingAssistantsDecoration::handles() { QList hs; foreach(KisPaintingAssistant* assistant, d->assistants) { foreach(const KisPaintingAssistantHandleSP handle, assistant->handles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } foreach(const KisPaintingAssistantHandleSP handle, assistant->sideHandles()) { if (!hs.contains(handle)) { hs.push_back(handle); } } } return hs; } QList KisPaintingAssistantsDecoration::assistants() { return d->assistants; } void KisPaintingAssistantsDecoration::setAssistantVisible(bool set) { d->assistantVisible=set; } void KisPaintingAssistantsDecoration::setOutlineVisible(bool set) { d->outlineVisible=set; } void KisPaintingAssistantsDecoration::setOnlyOneAssistantSnap(bool assistant) { d->snapOnlyOneAssistant = assistant; } bool KisPaintingAssistantsDecoration::assistantVisibility() { return d->assistantVisible; } bool KisPaintingAssistantsDecoration::outlineVisibility() { return d->outlineVisible; } void KisPaintingAssistantsDecoration::uncache() { foreach(KisPaintingAssistant* assistant, d->assistants) { assistant->uncache(); } } void KisPaintingAssistantsDecoration::toggleAssistantVisible() { setAssistantVisible(!assistantVisibility()); uncache(); } void KisPaintingAssistantsDecoration::toggleOutlineVisible() { setOutlineVisible(!outlineVisibility()); } diff --git a/krita/ui/kis_painting_assistants_manager.cpp b/krita/ui/kis_painting_assistants_manager.cpp index c2ee304b6b..4ac2d41104 100644 --- a/krita/ui/kis_painting_assistants_manager.cpp +++ b/krita/ui/kis_painting_assistants_manager.cpp @@ -1,99 +1,99 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painting_assistants_manager.h" #include "kis_painting_assistants_decoration.h" -#include "KisView.h" #include "KisViewManager.h" #include "kis_action_manager.h" +#include "kis_action.h" #include #include #include #include KisPaintingAssistantsManager::KisPaintingAssistantsManager(KisViewManager* view) : QObject(view) , m_imageView(0) { } KisPaintingAssistantsManager::~KisPaintingAssistantsManager() { } void KisPaintingAssistantsManager::setup(KisActionManager * actionManager) { m_toggleAssistant = new KisAction(i18n("Show Painting Assistants"), this); m_toggleAssistant->setCheckable(true); m_toggleAssistant->setActivationFlags(KisAction::ACTIVE_NODE); actionManager->addAction("view_toggle_painting_assistants", m_toggleAssistant); m_togglePreview = new KisAction(i18n("Show Assistant Previews"), this); m_togglePreview->setCheckable(true); m_togglePreview->setActivationFlags(KisAction::ACTIVE_NODE); actionManager->addAction("view_toggle_assistant_previews", m_togglePreview); updateAction(); } void KisPaintingAssistantsManager::setView(QPointer imageView) { if (m_imageView) { m_toggleAssistant->disconnect(); if (decoration()) { decoration()->disconnect(this); } } m_imageView = imageView; if (m_imageView && !decoration()) { KisPaintingAssistantsDecoration* deco = new KisPaintingAssistantsDecoration(m_imageView); m_imageView->canvasBase()->addDecoration(deco); } if (m_imageView && decoration()) { connect(m_toggleAssistant, SIGNAL(triggered()), decoration(), SLOT(toggleAssistantVisible())); connect(m_togglePreview, SIGNAL(triggered()), decoration(), SLOT(toggleOutlineVisible())); connect(decoration(), SIGNAL(assistantChanged()), SLOT(updateAction())); } updateAction(); } void KisPaintingAssistantsManager::updateAction() { if (decoration()) { bool enabled = !decoration()->assistants().isEmpty(); m_toggleAssistant->setChecked(decoration()->visible()); m_toggleAssistant->setEnabled(enabled); m_togglePreview->setChecked(decoration()->outlineVisibility()); m_togglePreview->setEnabled(enabled); } else { m_toggleAssistant->setEnabled(false); } } KisPaintingAssistantsDecoration* KisPaintingAssistantsManager::decoration() { if (m_imageView) { return m_imageView->canvasBase()->paintingAssistantsDecoration(); } return 0; } diff --git a/krita/ui/kis_painting_assistants_manager.h b/krita/ui/kis_painting_assistants_manager.h index ca284c59a1..14912a722f 100644 --- a/krita/ui/kis_painting_assistants_manager.h +++ b/krita/ui/kis_painting_assistants_manager.h @@ -1,56 +1,57 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2014 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTING_ASSISTANTS_MANAGER_H #define KIS_PAINTING_ASSISTANTS_MANAGER_H #include #include #include "KisView.h" -#include "kis_action.h" class KisViewManager; class KisPaintingAssistantsDecoration; +class KisAction; +class KisActionManager; class KisPaintingAssistantsManager : public QObject { Q_OBJECT public: KisPaintingAssistantsManager(KisViewManager* view); virtual ~KisPaintingAssistantsManager(); void setup(KisActionManager* actionManager); void setView(QPointer imageView); private Q_SLOTS: void updateAction(); private: KisPaintingAssistantsDecoration* decoration(); QPointer m_imageView; KisAction* m_toggleAssistant; KisAction* m_togglePreview; }; #endif // KIS_PAINTING_ASSISTANTS_MANAGER_H diff --git a/krita/ui/kis_paintop_box.h b/krita/ui/kis_paintop_box.h index fcddd6c04e..b701a89016 100644 --- a/krita/ui/kis_paintop_box.h +++ b/krita/ui/kis_paintop_box.h @@ -1,249 +1,247 @@ /* * kis_paintop_box.h - part of KImageShop/Krayon/Krita * * Copyright (c) 2004-2008 Boudewijn Rempt (boud@valdyas.org) * Copyright (C) 2011 Silvio Heinrich * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_BOX_H_ #define KIS_PAINTOP_BOX_H_ #include #include #include #include #include #include #include #include "kis_locked_properties_proxy.h" #include "kis_locked_properties_server.h" #include "kis_locked_properties.h" -#include "kis_config.h" #include "kritaui_export.h" class QToolButton; class QString; class QHBoxLayout; class KoColorSpace; class KoResource; class KoCanvasController; class KisViewManager; class KisCanvasResourceProvider; class KisPopupButton; class KisToolOptionsPopup; class KisPaintOpPresetsPopup; class KisPaintOpPresetsChooserPopup; class KisPaintOpConfigWidget; class KisCompositeOpComboBox; class KisWidgetChooser; class KisFavoriteResourceManager; -class KisLockedProperties; class KisAction; /** * This widget presents all paintops that a user can paint with. * Paintops represent real-world tools or the well-known Shoup * computer equivalents that do nothing but change color. * * To incorporate the dirty preset functionality and locked settings * the following slots are added * void slotReloadPreset(); void slotConfigurationItemChanged(); void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p); void slotDropLockedOption(KisPropertiesConfiguration* p); void slotDirtyPresetToggled(bool); Everytime a value is changed in a preset, the preset is made dirty through the onChange() function. For Locked Settings however, a changed Locked Setting will not cause a preset to become dirty. That is becuase it borrows its values from the KisLockedPropertiesServer. Hence the dirty state of the Preset is kept consistent before and after a writeConfiguration operation in most cases. * XXX: When we have a lot of paintops, replace the listbox * with a table, and for every category a combobox. * * XXX: instead of text, use pretty pictures. */ class KRITAUI_EXPORT KisPaintopBox : public QWidget { Q_OBJECT enum { ENABLE_PRESETS = 0x0001, DISABLE_PRESETS = 0x0002, ENABLE_COMPOSITEOP = 0x0004, DISABLE_COMPOSITEOP = 0x0008, ENABLE_OPACITY = 0x0010, DISABLE_OPACITY = 0x0020, ENABLE_FLOW = 0x0040, DISABLE_FLOW = 0x0080, ENABLE_SIZE = 0x0100, DISABLE_SIZE = 0x0200, ENABLE_ALL = 0x5555, DISABLE_ALL = 0xAAAA }; public: KisPaintopBox(KisViewManager* view, QWidget* parent, const char* name); ~KisPaintopBox(); void restoreResource(KoResource* resource); /** * Update the option widgets to the argument ones, removing the currently set widgets. */ void newOptionWidgets(const QList > & optionWidgetList); public Q_SLOTS: void slotColorSpaceChanged(const KoColorSpace* colorSpace); void slotInputDeviceChanged(const KoInputDevice & inputDevice); void slotCanvasResourceChanged(int key, const QVariant& v); void resourceSelected(KoResource* resource); KisFavoriteResourceManager *favoriteResourcesManager() { return m_favoriteResourceManager; } private: KoID currentPaintop(); void setCurrentPaintop(const KoID& paintop, KisPaintOpPresetSP preset = 0); void setCurrentPaintopAndReload(const KoID& paintop, KisPaintOpPresetSP preset); QPixmap paintopPixmap(const KoID& paintop); KoID defaultPaintOp(); KisPaintOpPresetSP defaultPreset(const KoID& paintOp); KisPaintOpPresetSP activePreset(const KoID& paintOp); void updateCompositeOp(QString compositeOpID, bool localUpdate = false); void setWidgetState(int flags); void setSliderValue(const QString& sliderID, qreal value); void sliderChanged(int n); private Q_SLOTS: void slotSaveActivePreset(); void slotUpdatePreset(); void slotSetupDefaultPreset(); void slotNodeChanged(const KisNodeSP node); void slotToggleEraseMode(bool checked); void slotSetCompositeMode(int index); void slotSetPaintop(const QString& paintOpId); void slotHorizontalMirrorChanged(bool value); void slotVerticalMirrorChanged(bool value); void slotSlider1Changed(); void slotSlider2Changed(); void slotSlider3Changed(); void slotToolChanged(KoCanvasController* canvas, int toolId); void slotOpacityChanged(qreal); void slotPreviousFavoritePreset(); void slotNextFavoritePreset(); void slotSwitchToPreviousPreset(); void slotUnsetEraseMode(); void slotToggleAlphaLockMode(bool); void toggleHighlightedButton(QToolButton* m_tool); void slotReloadPreset(); void slotConfigurationItemChanged(); void slotSaveLockedOptionToPreset(KisPropertiesConfiguration* p); void slotDropLockedOption(KisPropertiesConfiguration* p); void slotDirtyPresetToggled(bool); void slotEraserBrushSizeToggled(bool); void slotUpdateSelectionIcon(); private: KisCanvasResourceProvider* m_resourceProvider; QHBoxLayout* m_layout; QWidget* m_paintopWidget; KisPaintOpConfigWidget* m_optionWidget; KisPopupButton* m_toolOptionsPopupButton; KisPopupButton* m_brushEditorPopupButton; KisPopupButton* m_presetSelectorPopupButton; KisCompositeOpComboBox* m_cmbCompositeOp; QToolButton* m_eraseModeButton; QToolButton* m_alphaLockButton; QToolButton* m_hMirrorButton; QToolButton* m_vMirrorButton; KisToolOptionsPopup* m_toolOptionsPopup; KisPaintOpPresetsPopup* m_presetsPopup; KisPaintOpPresetsChooserPopup* m_presetsChooserPopup; KisViewManager* m_viewManager; KisPopupButton* m_workspaceWidget; KisWidgetChooser* m_sliderChooser[3]; QMap m_paintopOptionWidgets; KisFavoriteResourceManager* m_favoriteResourceManager; QToolButton* m_reloadButton; QString m_prevCompositeOpID; QString m_currCompositeOpID; KisNodeWSP m_previousNode; qreal normalBrushSize; // when toggling between eraser mode qreal eraserBrushSize; KisAction* m_hMirrorAction; KisAction* m_vMirrorAction; struct TabletToolID { TabletToolID(const KoInputDevice& dev) { uniqueID = dev.uniqueTabletId(); // Only the eraser is special, and we don't look at Cursor pointer = QTabletEvent::Pen; if (dev.pointer() == QTabletEvent::Eraser) { pointer = QTabletEvent::Eraser; } } bool operator == (const TabletToolID& id) const { return pointer == id.pointer && uniqueID == id.uniqueID; } bool operator < (const TabletToolID& id) const { if (uniqueID == id.uniqueID) return pointer < id.pointer; return uniqueID < id.uniqueID; } QTabletEvent::PointerType pointer; qint64 uniqueID; }; struct TabletToolData { KoID paintOpID; KisPaintOpPresetSP preset; }; typedef QMap TabletToolMap; typedef QMap PaintOpPresetMap; TabletToolMap m_tabletToolMap; PaintOpPresetMap m_paintOpPresetMap; TabletToolID m_currTabletToolID; bool m_presetsEnabled; bool m_blockUpdate; bool m_dirtyPresetsEnabled; bool m_eraserBrushSizeEnabled; }; #endif //KIS_PAINTOP_BOX_H_ diff --git a/krita/ui/kis_paintop_option.h b/krita/ui/kis_paintop_option.h index fa524266ea..dde6157419 100644 --- a/krita/ui/kis_paintop_option.h +++ b/krita/ui/kis_paintop_option.h @@ -1,115 +1,114 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_OPTION_H #define KIS_PAINTOP_OPTION_H #include #include #include #include class QWidget; -class QString; /** * Base interface for paintop options. A paintop option * can be enabled/disabled, has a configuration page * (for example, a curve), a user-visible name and can * be serialized and deserialized into KisPaintOpPresets * * Options are disabled by default. */ class KRITAUI_EXPORT KisPaintOpOption : public QObject { Q_OBJECT public: enum PaintopCategory { GENERAL, COLOR, TEXTURE, FILTER }; KisPaintOpOption(KisPaintOpOption::PaintopCategory category, bool checked); virtual ~KisPaintOpOption(); KisPaintOpOption::PaintopCategory category() const; virtual bool isCheckable() const; virtual bool isChecked() const; virtual void setChecked(bool checked); void setLocked(bool value); bool isLocked() const; /** * Reimplement this to use the image in the option widget */ virtual void setImage(KisImageWSP image); virtual void setNode(KisNodeWSP node); void startReadOptionSetting(const KisPropertiesConfiguration* setting); void startWriteOptionSetting(KisPropertiesConfiguration* setting) const; QWidget* configurationPage() const; protected: void setConfigurationPage(QWidget * page); protected: /** * Re-implement this to save the configuration to the paint configuration. */ virtual void writeOptionSetting(KisPropertiesConfiguration* setting) const { Q_UNUSED(setting); } /** * Re-implement this to set te widgets with the values in @param setting. */ virtual void readOptionSetting(const KisPropertiesConfiguration* setting) { Q_UNUSED(setting); } protected Q_SLOTS: void emitSettingChanged(); Q_SIGNALS: /** * emit this whenever a setting has changed. It will update the preview */ void sigSettingChanged(); protected: bool m_checkable; bool m_locked; private: struct Private; Private* const m_d; }; #endif diff --git a/krita/ui/kis_paintop_options_model.cpp b/krita/ui/kis_paintop_options_model.cpp index 678a730bd1..67ca66ae8e 100644 --- a/krita/ui/kis_paintop_options_model.cpp +++ b/krita/ui/kis_paintop_options_model.cpp @@ -1,107 +1,106 @@ /* This file is part of the KDE project * Copyright (c) 2010 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_paintop_options_model.h" -#include "kis_paintop_option.h" KisPaintOpOptionListModel::KisPaintOpOptionListModel(QObject *parent) : BaseOptionCategorizedListModel(parent) { } void KisPaintOpOptionListModel::addPaintOpOption(KisPaintOpOption *option, int widgetIndex, const QString &label) { QString category; switch(option->category()) { case KisPaintOpOption::GENERAL: category = i18nc("option category", "General"); break; case KisPaintOpOption::COLOR: category = i18nc("option category", "Color"); break; case KisPaintOpOption::TEXTURE: category = i18nc("option category", "Texture"); break; case KisPaintOpOption::FILTER: category = i18nc("option category", "Filter"); break; default: category = i18n("Unknown"); }; DataItem *item = categoriesMapper()->addEntry(category, KisOptionInfo(option, widgetIndex, label)); if (option->isCheckable()) { item->setCheckable(true); item->setChecked(option->isChecked()); } categoriesMapper()->expandAllCategories(); } QVariant KisPaintOpOptionListModel::data(const QModelIndex& idx, int role) const { if (!idx.isValid()) return false; DataItem *item = categoriesMapper()->itemFromRow(idx.row()); Q_ASSERT(item); // Lazy fetching of the real checked value (there are no notifications // when changing the pointop preset) if (role == Qt::CheckStateRole && item->isCheckable()) { bool realChecked = item->data()->option->isChecked(); if (realChecked != item->isChecked()) { item->setChecked(realChecked); } } return BaseOptionCategorizedListModel::data(idx, role); } bool KisPaintOpOptionListModel::setData(const QModelIndex& idx, const QVariant& value, int role) { if (!idx.isValid()) return false; DataItem *item = categoriesMapper()->itemFromRow(idx.row()); Q_ASSERT(item); if (role == Qt::CheckStateRole && item->isCheckable()) { item->data()->option->setChecked(value.toInt() == Qt::Checked); } return BaseOptionCategorizedListModel::setData(idx, value, role); } bool operator==(const KisOptionInfo& a, const KisOptionInfo& b) { if (a.index != b.index) return false; if (a.option->objectName() == b.option->objectName()) if (a.option->category() != b.option->category()) return false; if (a.option->isCheckable() != b.option->isCheckable()) return false; if (a.option->isChecked() != b.option->isChecked()) return false; return true; } void KisPaintOpOptionListModel::signalDataChanged(const QModelIndex& index) { emit dataChanged(index,index); } diff --git a/krita/ui/kis_selection_manager.cc b/krita/ui/kis_selection_manager.cc index ab37d85b8b..2c9ad1b62d 100644 --- a/krita/ui/kis_selection_manager.cc +++ b/krita/ui/kis_selection_manager.cc @@ -1,657 +1,656 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2007 Sven Langkamp * * The outline algorith uses the limn algorithm of fontutils by * Karl Berry and Kathryn Hargreaves * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_selection_manager.h" #include #include #include #include #include #include #include #include #include #include "KoCanvasController.h" #include "KoChannelInfo.h" #include "KoIntegerMaths.h" #include #include #include #include #include #include #include #include #include #include #include #include "kis_adjustment_layer.h" #include "kis_node_manager.h" #include "canvas/kis_canvas2.h" #include "kis_config.h" #include "kis_convolution_painter.h" #include "kis_convolution_kernel.h" #include "kis_debug.h" #include "KisDocument.h" #include "kis_fill_painter.h" #include "kis_group_layer.h" -#include "kis_image.h" #include "kis_layer.h" #include "kis_statusbar.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "kis_painter.h" #include "kis_transaction.h" #include "kis_selection.h" #include "kis_types.h" #include "kis_canvas_resource_provider.h" #include "kis_undo_adapter.h" #include "kis_pixel_selection.h" #include "flake/kis_shape_selection.h" #include "commands/kis_selection_commands.h" #include "kis_selection_mask.h" #include "flake/kis_shape_layer.h" #include "kis_selection_decoration.h" #include "canvas/kis_canvas_decoration.h" #include "kis_node_commands_adapter.h" #include "kis_iterator_ng.h" #include "kis_clipboard.h" #include "KisViewManager.h" #include "kis_selection_filters.h" #include "kis_figure_painting_tool_helper.h" #include "KisView.h" #include "actions/kis_selection_action_factories.h" #include "kis_action.h" #include "kis_action_manager.h" #include "operations/kis_operation_configuration.h" KisSelectionManager::KisSelectionManager(KisViewManager * view) : m_view(view), m_doc(0), m_imageView(0), m_adapter(new KisNodeCommandsAdapter(view)), m_copy(0), m_copyMerged(0), m_cut(0), m_paste(0), m_pasteNew(0), m_cutToNewLayer(0), m_selectAll(0), m_deselect(0), m_clear(0), m_reselect(0), m_invert(0), m_copyToNewLayer(0), m_fillForegroundColor(0), m_fillBackgroundColor(0), m_fillPattern(0), m_imageResizeToSelection(0), m_selectionDecoration(0) { m_clipboard = KisClipboard::instance(); } KisSelectionManager::~KisSelectionManager() { } void KisSelectionManager::setup(KisActionManager* actionManager) { m_cut = actionManager->createStandardAction(KStandardAction::Cut, this, SLOT(cut())); m_copy = actionManager->createStandardAction(KStandardAction::Copy, this, SLOT(copy())); m_paste = actionManager->createStandardAction(KStandardAction::Paste, this, SLOT(paste())); KisAction *copySharp = new KisAction(i18n("Copy (sharp)"), this); copySharp->setActivationFlags(KisAction::PIXELS_SELECTED); actionManager->addAction("copy_sharp", copySharp); connect(copySharp, SIGNAL(triggered()), this, SLOT(copySharp())); KisAction *cutSharp = new KisAction(i18n("Cut (sharp)"), this); cutSharp->setActivationFlags(KisAction::PIXELS_SELECTED); actionManager->addAction("cut_sharp", cutSharp); connect(cutSharp, SIGNAL(triggered()), this, SLOT(cutSharp())); m_pasteNew = new KisAction(i18n("Paste into &New Image"), this); actionManager->addAction("paste_new", m_pasteNew); m_pasteNew->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N)); connect(m_pasteNew, SIGNAL(triggered()), this, SLOT(pasteNew())); m_pasteAt = new KisAction(i18n("Paste at cursor"), this); actionManager->addAction("paste_at", m_pasteAt); connect(m_pasteAt, SIGNAL(triggered()), this, SLOT(pasteAt())); m_copyMerged = new KisAction(i18n("Copy merged"), this); m_copyMerged->setActivationFlags(KisAction::PIXELS_SELECTED); actionManager->addAction("copy_merged", m_copyMerged); m_copyMerged->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)); connect(m_copyMerged, SIGNAL(triggered()), this, SLOT(copyMerged())); m_selectAll = new KisAction(KisIconUtils::loadIcon("select-all"), i18n("Select &All"), this); actionManager->addAction("select_all", m_selectAll); m_selectAll->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); connect(m_selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); m_deselect = new KisAction(KisIconUtils::loadIcon("select-clear"), i18n("Deselect"), this); m_deselect->setActivationFlags(KisAction::PIXELS_SELECTED | KisAction::SHAPES_SELECTED); actionManager->addAction("deselect", m_deselect); m_deselect->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A)); connect(m_deselect, SIGNAL(triggered()), this, SLOT(deselect())); m_clear = new KisAction(KisIconUtils::loadIcon("select-clear"), i18n("Clear"), this); m_clear->setActivationFlags(KisAction::ACTIVE_IMAGE); actionManager->addAction("clear", m_clear); m_clear->setDefaultShortcut(QKeySequence((Qt::Key_Delete))); connect(m_clear, SIGNAL(triggered()), SLOT(clear())); m_reselect = new KisAction(i18n("&Reselect"), this); actionManager->addAction("reselect", m_reselect); m_reselect->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D)); connect(m_reselect, SIGNAL(triggered()), this, SLOT(reselect())); m_invert = new KisAction(i18n("&Invert Selection"), this); m_invert->setActivationFlags(KisAction::PIXEL_SELECTION_WITH_PIXELS); m_invert->setActivationConditions(KisAction::SELECTION_EDITABLE); m_invert->setOperationID("invertselection"); m_invert->setToolTip("foo"); actionManager->addAction("invert", m_invert); m_invert->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I)); actionManager->registerOperation(new KisInvertSelectionOperaton); m_copyToNewLayer = new KisAction(i18n("Copy Selection to New Layer"), this); m_copyToNewLayer->setActivationFlags(KisAction::PIXELS_SELECTED); actionManager->addAction("copy_selection_to_new_layer", m_copyToNewLayer); m_copyToNewLayer->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_J)); connect(m_copyToNewLayer, SIGNAL(triggered()), this, SLOT(copySelectionToNewLayer())); m_cutToNewLayer = new KisAction(i18n("Cut Selection to New Layer"), this); m_cutToNewLayer->setActivationFlags(KisAction::PIXELS_SELECTED); m_cutToNewLayer->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("cut_selection_to_new_layer", m_cutToNewLayer); m_cutToNewLayer->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_J)); connect(m_cutToNewLayer, SIGNAL(triggered()), this, SLOT(cutToNewLayer())); m_fillForegroundColor = new KisAction(i18n("Fill with Foreground Color"), this); m_fillForegroundColor->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillForegroundColor->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_foreground_color", m_fillForegroundColor); m_fillForegroundColor->setDefaultShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Backspace)); connect(m_fillForegroundColor, SIGNAL(triggered()), this, SLOT(fillForegroundColor())); m_fillBackgroundColor = new KisAction(i18n("Fill with Background Color"), this); m_fillBackgroundColor->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillBackgroundColor->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_background_color", m_fillBackgroundColor); m_fillBackgroundColor->setDefaultShortcut(QKeySequence(Qt::Key_Backspace)); connect(m_fillBackgroundColor, SIGNAL(triggered()), this, SLOT(fillBackgroundColor())); m_fillPattern = new KisAction(i18n("Fill with Pattern"), this); m_fillPattern->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillPattern->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_pattern", m_fillPattern); connect(m_fillPattern, SIGNAL(triggered()), this, SLOT(fillPattern())); m_fillForegroundColorOpacity = new KisAction(i18n("Fill with Foreground Color (Opacity)"), this); m_fillForegroundColorOpacity->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillForegroundColorOpacity->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_foreground_color_opacity", m_fillForegroundColorOpacity); m_fillForegroundColorOpacity->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Backspace)); connect(m_fillForegroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillForegroundColorOpacity())); m_fillBackgroundColorOpacity = new KisAction(i18n("Fill with Background Color (Opacity)"), this); m_fillBackgroundColorOpacity->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillBackgroundColorOpacity->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_background_color_opacity", m_fillBackgroundColorOpacity); m_fillBackgroundColorOpacity->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::Key_Backspace)); connect(m_fillBackgroundColorOpacity, SIGNAL(triggered()), this, SLOT(fillBackgroundColorOpacity())); m_fillPatternOpacity = new KisAction(i18n("Fill with Pattern (Opacity)"), this); m_fillPatternOpacity->setActivationFlags(KisAction::ACTIVE_DEVICE); m_fillPatternOpacity->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE); actionManager->addAction("fill_selection_pattern_opacity", m_fillPatternOpacity); connect(m_fillPatternOpacity, SIGNAL(triggered()), this, SLOT(fillPatternOpacity())); m_strokeShapes = new KisAction(i18nc("@action:inmenu", "Stro&ke selected shapes"), this); m_strokeShapes->setActivationFlags(KisAction::SHAPES_SELECTED); actionManager->addAction("stroke_shapes", m_strokeShapes); connect(m_strokeShapes, SIGNAL(triggered()), this, SLOT(paintSelectedShapes())); m_toggleDisplaySelection = new KisAction(i18n("Display Selection"), this); m_toggleDisplaySelection->setCheckable(true); m_toggleDisplaySelection->setActivationFlags(KisAction::ACTIVE_NODE); actionManager->addAction("toggle_display_selection", m_toggleDisplaySelection); m_toggleDisplaySelection->setDefaultShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); connect(m_toggleDisplaySelection, SIGNAL(triggered()), this, SLOT(toggleDisplaySelection())); m_toggleDisplaySelection->setChecked(true); m_imageResizeToSelection = new KisAction(i18n("Trim to Selection"), this); m_imageResizeToSelection->setActivationFlags(KisAction::PIXELS_SELECTED); actionManager->addAction("resizeimagetoselection", m_imageResizeToSelection); connect(m_imageResizeToSelection, SIGNAL(triggered()), this, SLOT(imageResizeToSelection())); KisAction *action = new KisAction(i18nc("@action:inmenu", "&Convert to Vector Selection"), this); action->setActivationFlags(KisAction::PIXEL_SELECTION_WITH_PIXELS); actionManager->addAction("convert_to_vector_selection", action); connect(action, SIGNAL(triggered()), SLOT(convertToVectorSelection())); action = new KisAction(i18nc("@action:inmenu", "Convert &Shapes to Vector Selection"), this); action->setActivationFlags(KisAction::SHAPES_SELECTED); actionManager->addAction("convert_shapes_to_vector_selection", action); connect(action, SIGNAL(triggered()), SLOT(convertShapesToVectorSelection())); action = new KisAction(i18nc("@action:inmenu", "&Convert to Shape"), this); action->setActivationFlags(KisAction::PIXEL_SELECTION_WITH_PIXELS); actionManager->addAction("convert_selection_to_shape", action); connect(action, SIGNAL(triggered()), SLOT(convertToShape())); m_toggleSelectionOverlayMode = new KisAction(i18nc("@action:inmenu", "&Toggle Selection Display Mode"), this); actionManager->addAction("toggle-selection-overlay-mode", m_toggleSelectionOverlayMode); connect(m_toggleSelectionOverlayMode, SIGNAL(triggered()), SLOT(slotToggleSelectionDecoration())); QClipboard *cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardDataChanged())); } void KisSelectionManager::setView(QPointerimageView) { if (m_imageView && m_imageView->canvasBase()) { disconnect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), this, SLOT(clipboardDataChanged())); KoSelection *selection = m_imageView->canvasBase()->globalShapeManager()->selection(); selection->disconnect(this, SLOT(shapeSelectionChanged())); KisSelectionDecoration *decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection")); if (decoration) { disconnect(SIGNAL(currentSelectionChanged()), decoration); } m_imageView->image()->undoAdapter()->disconnect(this); m_selectionDecoration = 0; } m_imageView = imageView; if (m_imageView) { KoSelection * selection = m_imageView->canvasBase()->globalShapeManager()->selection(); Q_ASSERT(selection); connect(selection, SIGNAL(selectionChanged()), this, SLOT(shapeSelectionChanged())); KisSelectionDecoration* decoration = qobject_cast(m_imageView->canvasBase()->decoration("selection")); if (!decoration) { decoration = new KisSelectionDecoration(m_imageView); decoration->setVisible(true); m_imageView->canvasBase()->addDecoration(decoration); } m_selectionDecoration = decoration; connect(this, SIGNAL(currentSelectionChanged()), decoration, SLOT(selectionChanged())); connect(m_imageView->image()->undoAdapter(), SIGNAL(selectionChanged()), SLOT(selectionChanged())); connect(m_imageView->canvasBase()->toolProxy(), SIGNAL(toolChanged(const QString&)), SLOT(clipboardDataChanged())); } } void KisSelectionManager::clipboardDataChanged() { m_view->updateGUI(); } bool KisSelectionManager::havePixelsSelected() { KisSelectionSP activeSelection = m_view->selection(); return activeSelection && !activeSelection->selectedRect().isEmpty(); } bool KisSelectionManager::havePixelsInClipboard() { return m_clipboard->hasClip(); } bool KisSelectionManager::haveShapesSelected() { if (m_view && m_view->canvasBase() && m_view->canvasBase()->shapeManager() && m_view->canvasBase()->shapeManager()->selection()->count()) { return m_view->canvasBase()->shapeManager()->selection()->count() > 0; } return false; } bool KisSelectionManager::haveShapesInClipboard() { KisShapeLayer *shapeLayer = dynamic_cast(m_view->activeLayer().data()); if (shapeLayer) { const QMimeData* data = QApplication::clipboard()->mimeData(); if (data) { QStringList mimeTypes = m_view->canvasBase()->toolProxy()->supportedPasteMimeTypes(); foreach(const QString & mimeType, mimeTypes) { if (data->hasFormat(mimeType)) { return true; } } } } return false; } bool KisSelectionManager::havePixelSelectionWithPixels() { KisSelectionSP selection = m_view->selection(); if (selection && selection->hasPixelSelection()) { return !selection->pixelSelection()->selectedRect().isEmpty(); } return false; } void KisSelectionManager::updateGUI() { Q_ASSERT(m_view); Q_ASSERT(m_clipboard); if (!m_view || !m_clipboard) return; bool havePixelsSelected = this->havePixelsSelected(); bool havePixelsInClipboard = this->havePixelsInClipboard(); bool haveShapesSelected = this->haveShapesSelected(); bool haveShapesInClipboard = this->haveShapesInClipboard(); bool haveDevice = m_view->activeDevice(); KisLayerSP activeLayer = m_view->activeLayer(); KisImageWSP image = activeLayer ? activeLayer->image() : 0; bool canReselect = image && image->canReselectGlobalSelection(); bool canDeselect = image && image->globalSelection(); m_clear->setEnabled(haveDevice || havePixelsSelected || haveShapesSelected); m_cut->setEnabled(havePixelsSelected || haveShapesSelected); m_copy->setEnabled(havePixelsSelected || haveShapesSelected); m_paste->setEnabled(havePixelsInClipboard || haveShapesInClipboard); m_pasteAt->setEnabled(havePixelsInClipboard || haveShapesInClipboard); // FIXME: how about pasting shapes? m_pasteNew->setEnabled(havePixelsInClipboard); m_selectAll->setEnabled(true); m_deselect->setEnabled(canDeselect); m_reselect->setEnabled(canReselect); // m_load->setEnabled(true); // m_save->setEnabled(havePixelsSelected); updateStatusBar(); emit signalUpdateGUI(); } void KisSelectionManager::updateStatusBar() { if (m_view && m_view->statusBar()) { m_view->statusBar()->setSelection(m_view->image()); } } void KisSelectionManager::selectionChanged() { m_view->updateGUI(); emit currentSelectionChanged(); } void KisSelectionManager::cut() { KisCutCopyActionFactory factory; factory.run(true, false, m_view); } void KisSelectionManager::copy() { KisCutCopyActionFactory factory; factory.run(false, false, m_view); } void KisSelectionManager::cutSharp() { KisCutCopyActionFactory factory; factory.run(true, true, m_view); } void KisSelectionManager::copySharp() { KisCutCopyActionFactory factory; factory.run(false, true, m_view); } void KisSelectionManager::copyMerged() { KisCopyMergedActionFactory factory; factory.run(m_view); } void KisSelectionManager::paste() { KisPasteActionFactory factory; factory.run(m_view); } void KisSelectionManager::pasteAt() { //XXX } void KisSelectionManager::pasteNew() { KisPasteNewActionFactory factory; factory.run(m_view); } void KisSelectionManager::selectAll() { KisSelectAllActionFactory factory; factory.run(m_view); } void KisSelectionManager::deselect() { KisDeselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::invert() { if(m_invert) m_invert->trigger(); } void KisSelectionManager::reselect() { KisReselectActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToVectorSelection() { KisSelectionToVectorActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertShapesToVectorSelection() { KisShapesToVectorSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::convertToShape() { KisSelectionToShapeActionFactory factory; factory.run(m_view); } void KisSelectionManager::clear() { KisClearActionFactory factory; factory.run(m_view); } void KisSelectionManager::fillForegroundColor() { KisFillActionFactory factory; factory.run("fg", m_view); } void KisSelectionManager::fillBackgroundColor() { KisFillActionFactory factory; factory.run("bg", m_view); } void KisSelectionManager::fillPattern() { KisFillActionFactory factory; factory.run("pattern", m_view); } void KisSelectionManager::fillForegroundColorOpacity() { KisFillActionFactory factory; factory.run("fg_opacity", m_view); } void KisSelectionManager::fillBackgroundColorOpacity() { KisFillActionFactory factory; factory.run("bg_opacity", m_view); } void KisSelectionManager::fillPatternOpacity() { KisFillActionFactory factory; factory.run("pattern_opacity", m_view); } void KisSelectionManager::copySelectionToNewLayer() { copy(); paste(); } void KisSelectionManager::cutToNewLayer() { cut(); paste(); } void KisSelectionManager::toggleDisplaySelection() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); m_selectionDecoration->toggleVisibility(); m_toggleDisplaySelection->blockSignals(true); m_toggleDisplaySelection->setChecked(m_selectionDecoration->visible()); m_toggleDisplaySelection->blockSignals(false); emit displaySelectionChanged(); } bool KisSelectionManager::displaySelection() { return m_toggleDisplaySelection->isChecked(); } void KisSelectionManager::shapeSelectionChanged() { KoShapeManager* shapeManager = m_view->canvasBase()->globalShapeManager(); KoSelection * selection = shapeManager->selection(); QList selectedShapes = selection->selectedShapes(); KoShapeStroke* border = new KoShapeStroke(0, Qt::lightGray); foreach(KoShape* shape, shapeManager->shapes()) { if (dynamic_cast(shape->parent())) { if (selectedShapes.contains(shape)) shape->setStroke(border); else shape->setStroke(0); } } m_view->updateGUI(); } void KisSelectionManager::imageResizeToSelection() { KisImageResizeToSelectionActionFactory factory; factory.run(m_view); } void KisSelectionManager::paintSelectedShapes() { KisImageWSP image = m_view->image(); if (!image) return; KisLayerSP layer = m_view->activeLayer(); if (!layer) return; QList shapes = m_view->canvasBase()->shapeManager()->selection()->selectedShapes(); KisPaintLayerSP paintLayer = new KisPaintLayer(image, i18n("Stroked Shapes"), OPACITY_OPAQUE_U8); KUndo2MagicString actionName = kundo2_i18n("Stroke Shapes"); m_adapter->beginMacro(actionName); m_adapter->addNode(paintLayer.data(), layer->parent().data(), layer.data()); KisFigurePaintingToolHelper helper(actionName, image, paintLayer.data(), m_view->resourceProvider()->resourceManager(), KisPainter::StrokeStyleBrush, KisPainter::FillStyleNone); foreach(KoShape* shape, shapes) { QTransform matrix = shape->absoluteTransformation(0) * QTransform::fromScale(image->xRes(), image->yRes()); QPainterPath mapedOutline = matrix.map(shape->outline()); helper.paintPainterPath(mapedOutline); } m_adapter->endMacro(); } void KisSelectionManager::slotToggleSelectionDecoration() { KIS_ASSERT_RECOVER_RETURN(m_selectionDecoration); KisSelectionDecoration::Mode mode = m_selectionDecoration->mode() ? KisSelectionDecoration::Ants : KisSelectionDecoration::Mask; m_selectionDecoration->setMode(mode); emit displaySelectionChanged(); } bool KisSelectionManager::showSelectionAsMask() const { if (m_selectionDecoration) { return m_selectionDecoration->mode() == KisSelectionDecoration::Mask; } return false; } diff --git a/krita/ui/kis_selection_manager.h b/krita/ui/kis_selection_manager.h index 34fe524eef..cf3d2e2bbe 100644 --- a/krita/ui/kis_selection_manager.h +++ b/krita/ui/kis_selection_manager.h @@ -1,176 +1,176 @@ /* * Copyright (c) 2004 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_SELECTION_MANAGER_ #define KIS_SELECTION_MANAGER_ #include #include +#include #include -# -#include "KisDocument.h" +#include "KisView.h" #include class KisActionManager; class KisAction; class QAction; class KoViewConverter; +class KisDocument; class KisViewManager; -class KisDoc; class KisClipboard; class KisNodeCommandsAdapter; class KisView; class KisSelectionFilter; class KisSelectionDecoration; /** * The selection manager is responsible selections * and the clipboard. */ class KRITAUI_EXPORT KisSelectionManager : public QObject { Q_OBJECT Q_PROPERTY(bool displaySelection READ displaySelection NOTIFY displaySelectionChanged); Q_PROPERTY(bool havePixelsSelected READ havePixelsSelected NOTIFY currentSelectionChanged); public: KisSelectionManager(KisViewManager * view); virtual ~KisSelectionManager(); void setup(KisActionManager* actionManager); void setView(QPointerimageView); public: /** * This function return if the selection should be displayed */ bool displaySelection(); bool showSelectionAsMask() const; public Q_SLOTS: void updateGUI(); void selectionChanged(); void clipboardDataChanged(); void cut(); void copy(); void cutSharp(); void copySharp(); void copyMerged(); void paste(); void pasteNew(); void pasteAt(); void cutToNewLayer(); void selectAll(); void deselect(); void invert(); void clear(); void fillForegroundColor(); void fillBackgroundColor(); void fillPattern(); void fillForegroundColorOpacity(); void fillBackgroundColorOpacity(); void fillPatternOpacity(); void reselect(); void convertToVectorSelection(); void convertShapesToVectorSelection(); void convertToShape(); void copySelectionToNewLayer(); void toggleDisplaySelection(); void shapeSelectionChanged(); void imageResizeToSelection(); void paintSelectedShapes(); void slotToggleSelectionDecoration(); Q_SIGNALS: void currentSelectionChanged(); void signalUpdateGUI(); void displaySelectionChanged(); public: bool havePixelsSelected(); bool havePixelsInClipboard(); bool haveShapesSelected(); bool haveShapesInClipboard(); /// Checks if the current selection is editabl and has some pixels selected in the pixel selection bool havePixelSelectionWithPixels(); // the following functions are needed for the siox tool // they might be also useful on its own void erode(); void dilate(); void paint(QPainter& gc, const KoViewConverter &converter); private: void fill(const KoColor& color, bool fillWithPattern, const QString& transactionText); void updateStatusBar(); void copyFromDevice(KisPaintDeviceSP device); void applySelectionFilter(KisSelectionFilter *filter); KisViewManager * m_view; KisDocument * m_doc; QPointerm_imageView; KisClipboard * m_clipboard; KisNodeCommandsAdapter* m_adapter; KisAction *m_copy; KisAction *m_copyMerged; KisAction *m_cut; KisAction *m_paste; KisAction *m_pasteAt; KisAction *m_pasteNew; KisAction *m_cutToNewLayer; KisAction *m_selectAll; KisAction *m_deselect; KisAction *m_clear; KisAction *m_reselect; KisAction *m_invert; KisAction *m_copyToNewLayer; KisAction *m_fillForegroundColor; KisAction *m_fillBackgroundColor; KisAction *m_fillPattern; KisAction *m_fillForegroundColorOpacity; KisAction *m_fillBackgroundColorOpacity; KisAction *m_fillPatternOpacity; KisAction *m_imageResizeToSelection; KisAction *m_strokeShapes; KisAction *m_toggleDisplaySelection; KisAction *m_toggleSelectionOverlayMode; QList m_pluginActions; KisSelectionDecoration *m_selectionDecoration; }; #endif // KIS_SELECTION_MANAGER_ diff --git a/krita/ui/kis_view_plugin.h b/krita/ui/kis_view_plugin.h index 4821c75ac4..ac23e4625c 100644 --- a/krita/ui/kis_view_plugin.h +++ b/krita/ui/kis_view_plugin.h @@ -1,55 +1,56 @@ /* * Copyright (c) 2013 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_VIEW_PLUGIN_H #define KIS_VIEW_PLUGIN_H #include -#include "operations/kis_operation_ui_factory.h" +#include class KisOperation; +class KisOperationUIFactory; class KisAction; class KisViewManager; /** * KisViewPlugin is the base for plugins which add actions to the view */ class KRITAUI_EXPORT KisViewPlugin : public QObject { Q_OBJECT public: KisViewPlugin(QObject* parent = 0); virtual ~KisViewPlugin(); protected: /** * adds an action to UI and action manager * @param name name of the action in the krita.rc file * @param action the action that should be added */ void addAction(const QString& name, KisAction* action); void addUIFactory(KisOperationUIFactory* factory); void addOperation(KisOperation* operation); KisViewManager* m_view; }; #endif // KIS_VIEW_PLUGIN_H diff --git a/krita/ui/kis_zoom_manager.cc b/krita/ui/kis_zoom_manager.cc index 76ccacf9d5..7f73e66c7d 100644 --- a/krita/ui/kis_zoom_manager.cc +++ b/krita/ui/kis_zoom_manager.cc @@ -1,301 +1,300 @@ /* * Copyright (C) 2006, 2010 Boudewijn Rempt * Copyright (C) 2009 Lukáš Tvrdý * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_zoom_manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisDocument.h" #include "KisViewManager.h" -#include "KisView.h" #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "kis_image.h" #include "kis_statusbar.h" #include "kis_config.h" #include "krita_utils.h" #include "kis_canvas_resource_provider.h" class KisZoomController : public KoZoomController { public: KisZoomController(KoCanvasController *co, KisCoordinatesConverter *zh, KActionCollection *actionCollection, KoZoomAction::SpecialButtons specialButtons, QObject *parent) : KoZoomController(co, zh, actionCollection, false, specialButtons, parent), m_converter(zh) { } protected: QSize documentToViewport(const QSizeF &size) { QRectF docRect(QPointF(), size); return m_converter->documentToWidget(docRect).toRect().size(); } private: KisCoordinatesConverter *m_converter; }; KisZoomManager::KisZoomManager(QPointer view, KoZoomHandler * zoomHandler, KoCanvasController * canvasController) : m_view(view) , m_zoomHandler(zoomHandler) , m_canvasController(canvasController) , m_horizontalRuler(0) , m_verticalRuler(0) , m_zoomAction(0) , m_zoomActionWidget(0) { } KisZoomManager::~KisZoomManager() { KisConfig cfg; cfg.setShowRulers(m_horizontalRuler->isVisible()); if (m_zoomActionWidget && !m_zoomActionWidget->parent()) { delete m_zoomActionWidget; } } void KisZoomManager::setup(KActionCollection * actionCollection) { KisImageWSP image = m_view->image(); if (!image) return; connect(image, SIGNAL(sigSizeChanged(const QPointF &, const QPointF &)), this, SLOT(setMinMaxZoom())); KisCoordinatesConverter *converter = dynamic_cast(m_zoomHandler); m_zoomController = new KisZoomController(m_canvasController, converter, actionCollection, KoZoomAction::AspectMode, this); m_zoomHandler->setZoomMode(KoZoomMode::ZOOM_PIXELS); m_zoomHandler->setZoom(1.0); m_zoomController->setPageSize(QSizeF(image->width() / image->xRes(), image->height() / image->yRes())); m_zoomController->setDocumentSize(QSizeF(image->width() / image->xRes(), image->height() / image->yRes()), true); m_zoomAction = m_zoomController->zoomAction(); setMinMaxZoom(); m_zoomActionWidget = m_zoomAction->createWidget(0); // Put the canvascontroller in a layout so it resizes with us QGridLayout * layout = new QGridLayout(m_view); layout->setSpacing(0); layout->setMargin(0); m_view->setLayout(layout); m_view->document()->setUnit(KoUnit(KoUnit::Pixel)); m_horizontalRuler = new KoRuler(m_view, Qt::Horizontal, m_zoomHandler); m_horizontalRuler->setShowMousePosition(true); m_horizontalRuler->createGuideToolConnection(m_view->canvasBase()); new KoRulerController(m_horizontalRuler, m_canvasController->canvas()->resourceManager()); m_verticalRuler = new KoRuler(m_view, Qt::Vertical, m_zoomHandler); m_verticalRuler->setShowMousePosition(true); m_verticalRuler->createGuideToolConnection(m_view->canvasBase()); QList unitActions = m_view->createChangeUnitActions(true); m_horizontalRuler->setPopupActionList(unitActions); m_verticalRuler->setPopupActionList(unitActions); connect(m_view->document(), SIGNAL(unitChanged(const KoUnit&)), SLOT(applyRulersUnit(const KoUnit&))); layout->addWidget(m_horizontalRuler, 0, 1); layout->addWidget(m_verticalRuler, 1, 0); layout->addWidget(static_cast(m_canvasController), 1, 1); connect(m_canvasController->proxyObject, SIGNAL(canvasOffsetXChanged(int)), this, SLOT(pageOffsetChanged())); connect(m_canvasController->proxyObject, SIGNAL(canvasOffsetYChanged(int)), this, SLOT(pageOffsetChanged())); connect(m_canvasController->proxyObject, SIGNAL(canvasMousePositionChanged(const QPoint &)), SLOT(mousePositionChanged(const QPoint &))); connect(m_zoomController, SIGNAL(zoomChanged(KoZoomMode::Mode, qreal)), this, SLOT(slotZoomChanged(KoZoomMode::Mode, qreal))); connect(m_zoomController, SIGNAL(aspectModeChanged(bool)), this, SLOT(changeAspectMode(bool))); applyRulersUnit(m_view->document()->unit()); KisConfig cfg; toggleShowRulers(cfg.showRulers()); } void KisZoomManager::mousePositionChanged(const QPoint &viewPos) { QPoint pt = viewPos - m_rulersOffset; m_horizontalRuler->updateMouseCoordinate(pt.x()); m_verticalRuler->updateMouseCoordinate(pt.y()); } bool KisZoomManager::horizontalRulerVisible() const { return m_horizontalRuler->isVisible(); } bool KisZoomManager::verticalRulerVisible() const { return m_verticalRuler->isVisible(); } void KisZoomManager::toggleShowRulers(bool show) { m_horizontalRuler->setVisible(show); m_verticalRuler->setVisible(show); } void KisZoomManager::applyRulersUnit(const KoUnit &baseUnit) { m_horizontalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->xRes())); m_verticalRuler->setUnit(KoUnit(baseUnit.type(), m_view->image()->yRes())); } void KisZoomManager::setMinMaxZoom() { KisImageWSP image = m_view->image(); if (!image) return; QSize imageSize = image->size(); qreal minDimension = qMin(imageSize.width(), imageSize.height()); qreal minZoom = qMin(100.0 / minDimension, 0.1); m_zoomAction->setMinimumZoom(minZoom); m_zoomAction->setMaximumZoom(90.0); } void KisZoomManager::updateGUI() { QRectF widgetRect = m_view->canvasBase()->coordinatesConverter()->imageRectInWidgetPixels(); QSize documentSize = m_view->canvasBase()->viewConverter()->viewToDocument(widgetRect).toAlignedRect().size(); m_horizontalRuler->setRulerLength(documentSize.width()); m_verticalRuler->setRulerLength(documentSize.height()); applyRulersUnit(m_horizontalRuler->unit()); } QWidget *KisZoomManager::zoomActionWidget() const { return m_zoomActionWidget; } void KisZoomManager::slotZoomChanged(KoZoomMode::Mode mode, qreal zoom) { Q_UNUSED(mode); Q_UNUSED(zoom); m_view->canvasBase()->notifyZoomChanged(); qreal humanZoom = zoom * 100.0; // XXX: KOMVC -- this is very irritating in MDI mode if (m_view->viewManager()) { m_view->viewManager()-> showFloatingMessage( i18nc("floating message about zoom", "Zoom: %1 %", KritaUtils::prettyFormatReal(humanZoom)), QIcon(), 500, KisFloatingMessage::Low, Qt::AlignCenter); } qreal scaleX, scaleY; m_view->canvasBase()->coordinatesConverter()->imageScale(&scaleX, &scaleY); if (scaleX != scaleY) { warnKrita << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY)); } // zoom by average of x and y const qreal effectiveZoom = 0.5 * (scaleX + scaleY); m_view->canvasBase()->resourceManager()->setResource(KisCanvasResourceProvider::EffectiveZoom, effectiveZoom); } void KisZoomManager::slotScrollAreaSizeChanged() { pageOffsetChanged(); updateGUI(); } void KisZoomManager::changeAspectMode(bool aspectMode) { KisImageWSP image = m_view->image(); KoZoomMode::Mode newMode = KoZoomMode::ZOOM_CONSTANT; qreal newZoom = m_zoomHandler->zoom(); qreal resolutionX = aspectMode ? image->xRes() : POINT_TO_INCH(static_cast(KoDpi::dpiX())); qreal resolutionY = aspectMode ? image->yRes() : POINT_TO_INCH(static_cast(KoDpi::dpiY())); m_zoomController->setZoom(newMode, newZoom, resolutionX, resolutionY); m_view->canvasBase()->notifyZoomChanged(); } void KisZoomManager::pageOffsetChanged() { QRectF widgetRect = m_view->canvasBase()->coordinatesConverter()->imageRectInWidgetPixels(); m_rulersOffset = widgetRect.topLeft().toPoint(); m_horizontalRuler->setOffset(m_rulersOffset.x()); m_verticalRuler->setOffset(m_rulersOffset.y()); } void KisZoomManager::zoomTo100() { m_zoomController->setZoom(KoZoomMode::ZOOM_CONSTANT, 1.0); m_view->canvasBase()->notifyZoomChanged(); } void KisZoomManager::showGuides(bool toggle) { m_view->document()->guidesData().setShowGuideLines(toggle); m_view->canvasBase()->canvasWidget()->update(); } diff --git a/krita/ui/kis_zoom_manager.h b/krita/ui/kis_zoom_manager.h index cd7ff13fab..ad0927d26b 100644 --- a/krita/ui/kis_zoom_manager.h +++ b/krita/ui/kis_zoom_manager.h @@ -1,94 +1,93 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_ZOOM_MANAGER #define KIS_ZOOM_MANAGER #include #include #include #include #include #include #include #include "KisView.h" class KoZoomHandler; -class QAction; class KoZoomAction; class KoRuler; class KoUnit; class KoCanvasController; class QPoint; /** The zoom manager handles all user actions related to zooming and unzooming. The actual computation of zoom levels and things are the job of KoZoomHandler or its descendants */ class KisZoomManager : public QObject { Q_OBJECT public: KisZoomManager(QPointer view, KoZoomHandler*, KoCanvasController *); ~KisZoomManager(); void setup(KActionCollection * actionCollection); void updateGUI(); KoZoomController * zoomController() const { return m_zoomController; } QWidget *zoomActionWidget() const; public Q_SLOTS: void slotZoomChanged(KoZoomMode::Mode mode, qreal zoom); void slotScrollAreaSizeChanged(); void toggleShowRulers(bool show); void mousePositionChanged(const QPoint &viewPos); void changeAspectMode(bool aspectMode); void pageOffsetChanged(); void zoomTo100(); void showGuides(bool toggle); void applyRulersUnit(const KoUnit &baseUnit); void setMinMaxZoom(); public: bool horizontalRulerVisible() const; bool verticalRulerVisible() const; private: QPointer m_view; KoZoomHandler * m_zoomHandler; KoCanvasController *m_canvasController; KoZoomController *m_zoomController; KoRuler * m_horizontalRuler; KoRuler * m_verticalRuler; KoZoomAction * m_zoomAction; QPointer m_zoomActionWidget; QPoint m_rulersOffset; }; #endif diff --git a/krita/ui/kra/kis_kra_loader.h b/krita/ui/kra/kis_kra_loader.h index 57f7b9b2b8..69bf4c2de7 100644 --- a/krita/ui/kra/kis_kra_loader.h +++ b/krita/ui/kra/kis_kra_loader.h @@ -1,106 +1,105 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2007 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_KRA_LOADER_H #define KIS_KRA_LOADER_H class QString; class QStringList; #include "KoXmlReaderForward.h" class KoStore; class KisDocument; -class KisNode; class KoColorSpace; class KisPaintingAssistant; #include #include /** * Load old-style 1.x .kra files. Updated for 2.0, let's try to stay * compatible. But 2.0 won't be able to save 1.x .kra files unless we * implement an export filter. */ class KRITAUI_EXPORT KisKraLoader { public: KisKraLoader(KisDocument * document, int syntaxVersion); ~KisKraLoader(); /** * Loading is done in two steps: first all xml is loaded, then, in finishLoading, * the actual layer data is loaded. */ KisImageWSP loadXML(const KoXmlElement& elem); void loadBinaryData(KoStore* store, KisImageWSP image, const QString & uri, bool external); vKisNodeSP selectedNodes() const; // it's neater to follow the same design as with selectedNodes, so let's have a getter here QList assistants() const; /// if empty, loading didn't fail... QStringList errorMessages() const; private: // this needs to be private, for neatness sake void loadAssistants(KoStore* store, const QString & uri, bool external); KisNodeSP loadNodes(const KoXmlElement& element, KisImageWSP image, KisNodeSP parent); KisNodeSP loadNode(const KoXmlElement& elem, KisImageWSP image, KisNodeSP parent); KisNodeSP loadPaintLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadGroupLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadAdjustmentLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadShapeLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadGeneratorLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadCloneLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, const KoColorSpace* cs, quint32 opacity); KisNodeSP loadFilterMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadTransformMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadTransparencyMask(const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadSelectionMask(KisImageWSP image, const KoXmlElement& elem, KisNodeSP parent); KisNodeSP loadFileLayer(const KoXmlElement& elem, KisImageWSP image, const QString& name, quint32 opacity); void loadCompositions(const KoXmlElement& elem, KisImageWSP image); void loadAssistantsList(const KoXmlElement& elem); private: struct Private; Private * const m_d; }; #endif diff --git a/krita/ui/opengl/kis_opengl_canvas2.h b/krita/ui/opengl/kis_opengl_canvas2.h index 391ec1b4c8..9336020e52 100644 --- a/krita/ui/opengl/kis_opengl_canvas2.h +++ b/krita/ui/opengl/kis_opengl_canvas2.h @@ -1,120 +1,119 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * Copyright (C) Michael Abrahams , (C) 2015 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_OPENGL_CANVAS_2_H #define KIS_OPENGL_CANVAS_2_H #include #ifdef HAVE_OPENGL #include #include #include #include "canvas/kis_canvas_widget_base.h" #include "opengl/kis_opengl_image_textures.h" #include "kritaui_export.h" #include "kis_ui_types.h" class QWidget; -class QPaintEvent; class KisCanvas2; class KisDisplayColorConverter; class QOpenGLShaderProgram; class QPainterPath; /** * KisOpenGLCanvas is the widget that shows the actual image using OpenGL * * NOTE: if you change something in the event handling here, also change it * in the qpainter canvas. * */ class KRITAUI_EXPORT KisOpenGLCanvas2 : public QOpenGLWidget, public QOpenGLFunctions, public KisCanvasWidgetBase { Q_OBJECT public: KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter); virtual ~KisOpenGLCanvas2(); public: // QOpenGLWidget void resizeGL(int width, int height); void initializeGL(); void paintGL(); virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; virtual void inputMethodEvent(QInputMethodEvent *event); public: void initializeCheckerShader(); void initializeDisplayShader(); void renderCanvasGL(); void renderDecorations(QPainter *painter); void paintToolOutline(const QPainterPath &path); public: // Implement kis_abstract_canvas_widget interface void setDisplayFilter(KisDisplayFilter* displayFilter); void setWrapAroundViewingMode(bool value); void channelSelectionChanged(QBitArray channelFlags); void setDisplayProfile(KisDisplayColorConverter *colorConverter); void disconnectCurrentCanvas(); void finishResizingImage(qint32 w, qint32 h); KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags); QRect updateCanvasProjection(KisUpdateInfoSP info); QWidget *widget() { return this; } bool isBusy() const; void setDisplayFilterImpl(KisDisplayFilter* displayFilter, bool initializing); private Q_SLOTS: void slotConfigChanged(); protected: // KisCanvasWidgetBase virtual bool callFocusNextPrevChild(bool next); private: void reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log); QOpenGLShaderProgram *getCursorShader(); void drawImage(); void drawCheckers(); QByteArray buildFragmentShader(); private: struct Private; Private * const d; }; #endif // HAVE_OPENGL #endif // KIS_OPENGL_CANVAS_2_H diff --git a/krita/ui/opengl/kis_texture_tile.cpp b/krita/ui/opengl/kis_texture_tile.cpp index aa80623fae..6f8d100fd4 100644 --- a/krita/ui/opengl/kis_texture_tile.cpp +++ b/krita/ui/opengl/kis_texture_tile.cpp @@ -1,330 +1,332 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define GL_GLEXT_PROTOTYPES #include "kis_texture_tile.h" +#include "kis_texture_tile_update_info.h" #ifdef HAVE_OPENGL #include #include + #ifndef GL_BGRA #define GL_BGRA 0x814F #endif inline QRectF relativeRect(const QRect &br /* baseRect */, const QRect &cr /* childRect */, const KisGLTexturesInfo *texturesInfo) { const qreal x = qreal(cr.x() - br.x()) / texturesInfo->width; const qreal y = qreal(cr.y() - br.y()) / texturesInfo->height; const qreal w = qreal(cr.width()) / texturesInfo->width; const qreal h = qreal(cr.height()) / texturesInfo->height; return QRectF(x, y, w, h); } KisTextureTile::KisTextureTile(QRect imageRect, const KisGLTexturesInfo *texturesInfo, const QByteArray &fillData, FilterMode filter, bool useBuffer, int numMipmapLevels, QOpenGLFunctions *fcn) : m_textureId(0) #ifdef USE_PIXEL_BUFFERS , m_glBuffer(0) #endif , m_tileRectInImagePixels(imageRect) , m_filter(filter) , m_texturesInfo(texturesInfo) , m_needsMipmapRegeneration(false) , m_useBuffer(useBuffer) , m_numMipmapLevels(numMipmapLevels) , f(fcn) { const GLvoid *fd = fillData.constData(); m_textureRectInImagePixels = stretchRect(m_tileRectInImagePixels, texturesInfo->border); m_tileRectInTexturePixels = relativeRect(m_textureRectInImagePixels, m_tileRectInImagePixels, m_texturesInfo); f->glGenTextures(1, &m_textureId); f->glBindTexture(GL_TEXTURE_2D, m_textureId); setTextureParameters(); #ifdef USE_PIXEL_BUFFERS createTextureBuffer(fillData.constData(), fillData.size()); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; #endif f->glTexImage2D(GL_TEXTURE_2D, 0, m_texturesInfo->internalFormat, m_texturesInfo->width, m_texturesInfo->height, 0, m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif setNeedsMipmapRegeneration(); } KisTextureTile::~KisTextureTile() { #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { delete m_glBuffer; } #endif f->glDeleteTextures(1, &m_textureId); } void KisTextureTile::bindToActiveTexture() { f->glBindTexture(GL_TEXTURE_2D, m_textureId); if (m_needsMipmapRegeneration) { f->glGenerateMipmap(GL_TEXTURE_2D); m_needsMipmapRegeneration = false; } } void KisTextureTile::setNeedsMipmapRegeneration() { if (m_filter == TrilinearFilterMode || m_filter == HighQualityFiltering) { m_needsMipmapRegeneration = true; } } void KisTextureTile::update(const KisTextureTileUpdateInfo &updateInfo) { f->initializeOpenGLFunctions(); f->glBindTexture(GL_TEXTURE_2D, m_textureId); setTextureParameters(); const GLvoid *fd = updateInfo.data(); #ifdef USE_PIXEL_BUFFERS if (!m_glBuffer) { createTextureBuffer((const char*)updateInfo.data(), updateInfo.patchPixelsLength()); } #endif if (updateInfo.isEntireTileUpdated()) { #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->bind(); m_glBuffer->allocate(updateInfo.patchPixelsLength()); void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, fd, updateInfo.patchPixelsLength()); m_glBuffer->unmap(); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; } #endif f->glTexImage2D(GL_TEXTURE_2D, 0, m_texturesInfo->internalFormat, m_texturesInfo->width, m_texturesInfo->height, 0, m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif } else { QPoint patchOffset = updateInfo.patchOffset(); QSize patchSize = updateInfo.patchSize(); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->bind(); quint32 size = patchSize.width() * patchSize.height() * updateInfo.pixelSize(); m_glBuffer->allocate(size); void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, fd, size); m_glBuffer->unmap(); // we set fill data to 0 so the next glTexImage2D call uses our buffer fd = 0; } #endif f->glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), patchOffset.y(), patchSize.width(), patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, fd); #ifdef USE_PIXEL_BUFFERS if (m_useBuffer) { m_glBuffer->release(); } #endif } /** * On the boundaries of KisImage, there is a border-effect as well. * So we just repeat the bounding pixels of the image to make * bilinear interpolator happy. */ /** * WARN: The width of the stripes will be equal to the broder * width of the tiles. */ const int pixelSize = updateInfo.pixelSize(); const QRect imageRect = updateInfo.imageRect(); const QPoint patchOffset = updateInfo.patchOffset(); const QSize patchSize = updateInfo.patchSize(); const QRect patchRect = QRect(m_textureRectInImagePixels.topLeft() + patchOffset, patchSize); const QSize tileSize = updateInfo.tileRect().size(); if(imageRect.top() == patchRect.top()) { int start = 0; int end = patchOffset.y() - 1; for (int i = start; i <= end; i++) { f->glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), i, patchSize.width(), 1, m_texturesInfo->format, m_texturesInfo->type, updateInfo.data()); } } if (imageRect.bottom() == patchRect.bottom()) { int shift = patchSize.width() * (patchSize.height() - 1) * pixelSize; int start = patchOffset.y() + patchSize.height(); int end = tileSize.height() - 1; for (int i = start; i < end; i++) { f->glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), i, patchSize.width(), 1, m_texturesInfo->format, m_texturesInfo->type, updateInfo.data() + shift); } } if (imageRect.left() == patchRect.left()) { QByteArray columnBuffer(patchSize.height() * pixelSize, 0); quint8 *srcPtr = updateInfo.data(); quint8 *dstPtr = (quint8*) columnBuffer.data(); for(int i = 0; i < patchSize.height(); i++) { memcpy(dstPtr, srcPtr, pixelSize); srcPtr += patchSize.width() * pixelSize; dstPtr += pixelSize; } int start = 0; int end = patchOffset.x() - 1; for (int i = start; i <= end; i++) { f->glTexSubImage2D(GL_TEXTURE_2D, 0, i, patchOffset.y(), 1, patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, columnBuffer.constData()); } } if (imageRect.right() == patchRect.right()) { QByteArray columnBuffer(patchSize.height() * pixelSize, 0); quint8 *srcPtr = updateInfo.data() + (patchSize.width() - 1) * pixelSize; quint8 *dstPtr = (quint8*) columnBuffer.data(); for(int i = 0; i < patchSize.height(); i++) { memcpy(dstPtr, srcPtr, pixelSize); srcPtr += patchSize.width() * pixelSize; dstPtr += pixelSize; } int start = patchOffset.x() + patchSize.width(); int end = tileSize.width() - 1; for (int i = start; i <= end; i++) { f->glTexSubImage2D(GL_TEXTURE_2D, 0, i, patchOffset.y(), 1, patchSize.height(), m_texturesInfo->format, m_texturesInfo->type, columnBuffer.constData()); } } setNeedsMipmapRegeneration(); } #ifdef USE_PIXEL_BUFFERS void KisTextureTile::createTextureBuffer(const char *data, int size) { if (m_useBuffer) { if (!m_glBuffer) { m_glBuffer = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); m_glBuffer->setUsagePattern(QOpenGLBuffer::DynamicDraw); m_glBuffer->create(); m_glBuffer->bind(); m_glBuffer->allocate(size); } void *vid = m_glBuffer->map(QOpenGLBuffer::WriteOnly); memcpy(vid, data, size); m_glBuffer->unmap(); } else { m_glBuffer = 0; } } #endif #endif /* HAVE_OPENGL */ diff --git a/krita/ui/opengl/kis_texture_tile.h b/krita/ui/opengl/kis_texture_tile.h index adf09cdf58..7b5808ce5b 100644 --- a/krita/ui/opengl/kis_texture_tile.h +++ b/krita/ui/opengl/kis_texture_tile.h @@ -1,140 +1,140 @@ /* * Copyright (c) 2010 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TEXTURE_TILE_H_ #define KIS_TEXTURE_TILE_H_ #include #ifdef HAVE_OPENGL -#include "kis_texture_tile_update_info.h" +class KisTextureTileUpdateInfo; #include #include #include #if !defined(QT_OPENGL_ES) #define USE_PIXEL_BUFFERS #include #endif struct KisGLTexturesInfo { // real width and height int width; int height; // width and height minus border padding? int effectiveWidth; int effectiveHeight; // size of the border padding int border; GLint internalFormat; GLint format; GLint type; }; inline QRect stretchRect(const QRect &rc, int delta) { return rc.adjusted(-delta, -delta, delta, delta); } class KisTextureTile { public: enum FilterMode { NearestFilterMode, // nearest BilinearFilterMode, // linear, no mipmap TrilinearFilterMode, // LINEAR_MIPMAP_LINEAR HighQualityFiltering // Mipmaps + custom shader }; KisTextureTile(QRect imageRect, const KisGLTexturesInfo *texturesInfo, const QByteArray &fillData, FilterMode mode, bool useBuffer, int numMipmapLevels, QOpenGLFunctions *f); ~KisTextureTile(); void setUseBuffer(bool useBuffer) { m_useBuffer = useBuffer; } void setNumMipmapLevels(int num) { m_numMipmapLevels = num; } void update(const KisTextureTileUpdateInfo &updateInfo); inline QRect tileRectInImagePixels() { return m_tileRectInImagePixels; } inline QRect textureRectInImagePixels() { return m_textureRectInImagePixels; } inline QRectF tileRectInTexturePixels() { return m_tileRectInTexturePixels; } inline void setTextureParameters() { if(f) { f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, m_numMipmapLevels); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, m_numMipmapLevels); f->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } } void bindToActiveTexture(); private: void setNeedsMipmapRegeneration(); GLuint m_textureId; #ifdef USE_PIXEL_BUFFERS void createTextureBuffer(const char*data, int size); QOpenGLBuffer *m_glBuffer; #endif QRect m_tileRectInImagePixels; QRectF m_tileRectInTexturePixels; QRect m_textureRectInImagePixels; FilterMode m_filter; const KisGLTexturesInfo *m_texturesInfo; bool m_needsMipmapRegeneration; bool m_useBuffer; int m_numMipmapLevels; QOpenGLFunctions *f; Q_DISABLE_COPY(KisTextureTile) }; #endif /* HAVE_OPENGL */ #endif /* KIS_TEXTURE_TILE_H_ */ diff --git a/krita/ui/operations/kis_operation_ui_factory.h b/krita/ui/operations/kis_operation_ui_factory.h index 9a4d36e41a..3b31104591 100644 --- a/krita/ui/operations/kis_operation_ui_factory.h +++ b/krita/ui/operations/kis_operation_ui_factory.h @@ -1,52 +1,55 @@ /* * Copyright (c) 2013 Sven Langkamp * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_OPERATION_UI_FACTORY_H #define KIS_OPERATION_UI_FACTORY_H -#include "kis_operation_configuration.h" +#include "kritaui_export.h" +#include + class KisViewManager; +class KisOperationConfiguration; class KRITAUI_EXPORT KisOperationUIFactory { public: /** * Construct a Ui factory * @param id the id for the ui, has to be the same as the operation id of the KisAction */ KisOperationUIFactory(const QString &id); virtual ~KisOperationUIFactory(); /** * id for the UI registry */ QString id() const; /** * Fetch the configuration for a QWidget or other UI * @param view the view * @param configuration the into which the setting will be written */ virtual bool fetchConfiguration(KisViewManager* view, KisOperationConfiguration* configuration) = 0; private: class Private; Private* const d; }; #endif // KIS_OPERATION_UI_FACTORY_H diff --git a/krita/ui/ora/kis_open_raster_stack_load_visitor.h b/krita/ui/ora/kis_open_raster_stack_load_visitor.h index 0ddee4de29..c377dd4022 100644 --- a/krita/ui/ora/kis_open_raster_stack_load_visitor.h +++ b/krita/ui/ora/kis_open_raster_stack_load_visitor.h @@ -1,56 +1,53 @@ /* * Copyright (c) 2006 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_OPEN_RASTER_STACK_LOAD_VISITOR_H_ #define KIS_OPEN_RASTER_STACK_LOAD_VISITOR_H_ #include "kis_global.h" #include "kis_types.h" #include class QDomElement; -class KisAdjustmentLayer; class KisUndoStore; -class KisGroupLayer; class KisOpenRasterLoadContext; -class KisPaintLayer; class KRITAUI_EXPORT KisOpenRasterStackLoadVisitor { public: KisOpenRasterStackLoadVisitor(KisUndoStore *undoStore, KisOpenRasterLoadContext* orlc); virtual ~KisOpenRasterStackLoadVisitor(); public: void loadImage(); void loadPaintLayer(const QDomElement& elem, KisPaintLayerSP pL); void loadAdjustmentLayer(const QDomElement& elem, KisAdjustmentLayerSP pL); void loadGroupLayer(const QDomElement& elem, KisGroupLayerSP gL); KisImageWSP image(); vKisNodeSP activeNodes(); private: void loadLayerInfo(const QDomElement& elem, KisLayerSP layer); struct Private; Private* const d; }; #endif // KIS_LAYER_VISITOR_H_ diff --git a/krita/ui/recorder/kis_recorded_action_creator_factory_registry.h b/krita/ui/recorder/kis_recorded_action_creator_factory_registry.h index 5ce78f048d..5e1e4c48b2 100644 --- a/krita/ui/recorder/kis_recorded_action_creator_factory_registry.h +++ b/krita/ui/recorder/kis_recorded_action_creator_factory_registry.h @@ -1,56 +1,55 @@ /* * Copyright (c) 2009,2011 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_RECORDED_ACTION_CREATOR_FACTORY_REGISTRY_H_ #define _KIS_RECORDED_ACTION_CREATOR_FACTORY_REGISTRY_H_ #include #include -class KisRecordedAction; class KisRecordedActionCreatorFactory; /** * This class allow to create a creator for a specific recorded action. * */ class KRITAUI_EXPORT KisRecordedActionCreatorFactoryRegistry { public: KisRecordedActionCreatorFactoryRegistry(); ~KisRecordedActionCreatorFactoryRegistry(); static KisRecordedActionCreatorFactoryRegistry* instance(); /** * Add a factory of action creator. */ void add(KisRecordedActionCreatorFactory* factory); /** * @return an creator for the given action, or a null pointer if there is * no factory for that action. */ KisRecordedActionCreatorFactory* get(const QString& _id) const; /** * @return the list of creators */ QList creators() const; private: struct Private; Private* const d; }; #endif diff --git a/krita/ui/tests/kis_shape_controller_test.h b/krita/ui/tests/kis_shape_controller_test.h index d4378ea62e..458e9c75c4 100644 --- a/krita/ui/tests/kis_shape_controller_test.h +++ b/krita/ui/tests/kis_shape_controller_test.h @@ -1,46 +1,45 @@ /* * Copyright (C) 2007 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISSHAPECONTROLLER_TEST_H #define KISSHAPECONTROLLER_TEST_H #include "kis_dummies_facade_base_test.h" class KisDocument; class KisNameServer; -class KisShapeController; class KisShapeControllerTest : public KisDummiesFacadeBaseTest { Q_OBJECT public: ~KisShapeControllerTest(); protected: KisDummiesFacadeBase* dummiesFacadeFactory(); void destroyDummiesFacade(KisDummiesFacadeBase *dummiesFacade); private: KisDocument *m_doc; KisNameServer *m_nameServer; }; #endif diff --git a/krita/ui/thememanager.h b/krita/ui/thememanager.h index 2fe4bf44cb..0ad228804a 100644 --- a/krita/ui/thememanager.h +++ b/krita/ui/thememanager.h @@ -1,87 +1,86 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-08-02 * Description : theme manager * * Copyright (C) 2006-2011 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef THEMEMANAGER_H #define THEMEMANAGER_H // Qt includes #include #include #include // KDE includes #include class KActionCollection; class KActionMenu; namespace Digikam { -class Theme; class ThemeManager : public QObject { Q_OBJECT public: explicit ThemeManager(QObject *parent); ~ThemeManager(); QString currentThemeName() const; void setCurrentTheme(const QString& name); QString defaultThemeName() const; void setThemeMenuAction(KActionMenu* const action); void registerThemeActions(KActionCollection *actionCollection); Q_SIGNALS: void signalThemeChanged(); private Q_SLOTS: void slotChangePalette(); void slotSettingsChanged(); private: void populateThemeMenu(); QPixmap createSchemePreviewIcon(const KSharedConfigPtr& config); QString currentKDEdefaultTheme() const; void updateCurrentKDEdefaultThemePreview(); private: class ThemeManagerPriv; ThemeManagerPriv* const d; }; } // namespace Digikam #endif /* THEMEMANAGER_H */ diff --git a/krita/ui/tool/kis_painting_information_builder.h b/krita/ui/tool/kis_painting_information_builder.h index 77816b32a2..4d92d1a9bb 100644 --- a/krita/ui/tool/kis_painting_information_builder.h +++ b/krita/ui/tool/kis_painting_information_builder.h @@ -1,110 +1,109 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_PAINTING_INFORMATION_BUILDER_H #define __KIS_PAINTING_INFORMATION_BUILDER_H #include #include #include "kis_types.h" #include "kritaui_export.h" #include "kis_paint_information.h" class KoPointerEvent; -class KisTool; class KisToolFreehand; class KisCoordinatesConverter; class KisSpeedSmoother; class KRITAUI_EXPORT KisPaintingInformationBuilder : public QObject { Q_OBJECT public: KisPaintingInformationBuilder(); ~KisPaintingInformationBuilder(); KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed); KisPaintInformation continueStroke(KoPointerEvent *event, int timeElapsed); KisPaintInformation hover(const QPointF &imagePoint, const KoPointerEvent *event); protected Q_SLOTS: void updateSettings(); protected: virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint); virtual QPointF documentToImage(const QPointF &point); virtual QPointF imageToView(const QPointF &point); virtual qreal calculatePerspective(const QPointF &documentPoint); private: KisPaintInformation createPaintingInformation(KoPointerEvent *event, int timeElapsed); /** * Defines how many discrete samples are stored in a precomputed array * of different pressures. */ static const int LEVEL_OF_PRESSURE_RESOLUTION; qreal pressureToCurve(qreal pressure); private: QVector m_pressureSamples; QPointF m_startPoint; QScopedPointer m_speedSmoother; }; class KRITAUI_EXPORT KisConverterPaintingInformationBuilder : public KisPaintingInformationBuilder { Q_OBJECT public: KisConverterPaintingInformationBuilder(const KisCoordinatesConverter *converter); protected: virtual QPointF documentToImage(const QPointF &point); virtual QPointF imageToView(const QPointF &point); private: const KisCoordinatesConverter *m_converter; }; class KRITAUI_EXPORT KisToolFreehandPaintingInformationBuilder : public KisPaintingInformationBuilder { Q_OBJECT public: KisToolFreehandPaintingInformationBuilder(KisToolFreehand *tool); protected: virtual QPointF documentToImage(const QPointF &point); virtual QPointF imageToView(const QPointF &point); virtual QPointF adjustDocumentPoint(const QPointF &point, const QPointF &startPoint); virtual qreal calculatePerspective(const QPointF &documentPoint); private: KisToolFreehand *m_tool; }; #endif /* __KIS_PAINTING_INFORMATION_BUILDER_H */ diff --git a/krita/ui/tool/kis_rectangle_constraint_widget.cpp b/krita/ui/tool/kis_rectangle_constraint_widget.cpp index fdb08e52bc..0a7eb8faaf 100644 --- a/krita/ui/tool/kis_rectangle_constraint_widget.cpp +++ b/krita/ui/tool/kis_rectangle_constraint_widget.cpp @@ -1,71 +1,72 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_rectangle_constraint_widget.h" +#include "kis_tool_rectangle_base.h" #include KisRectangleConstraintWidget::KisRectangleConstraintWidget(QWidget *parent, KisToolRectangleBase *tool) : QWidget(parent) { m_tool = tool; setupUi(this); chkHeight->setIcon(KisIconUtils::loadIcon("height_icon")); chkWidth->setIcon(KisIconUtils::loadIcon("width_icon")); chkRatio->setIcon(KisIconUtils::loadIcon("ratio_icon")); connect(chkWidth, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(chkHeight, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(chkRatio, SIGNAL(toggled(bool)), this, SLOT(inputsChanged(void))); connect(intWidth, SIGNAL(valueChanged(int)), this, SLOT(inputsChanged(void))); connect(intHeight, SIGNAL(valueChanged(int)), this, SLOT(inputsChanged(void))); connect(doubleRatio, SIGNAL(valueChanged(double)), this, SLOT(inputsChanged(void))); connect(this, SIGNAL(constraintsChanged(bool,bool,bool,float,float,float)), m_tool, SLOT(constraintsChanged(bool,bool,bool,float,float,float))); connect(m_tool, SIGNAL(rectangleChanged(QRectF)), this, SLOT(rectangleChanged(QRectF))); } void KisRectangleConstraintWidget::inputsChanged() { emit constraintsChanged( chkRatio->isChecked(), chkWidth->isChecked(), chkHeight->isChecked(), doubleRatio->value(), intWidth->value(), intHeight->value() ); } void KisRectangleConstraintWidget::rectangleChanged(const QRectF &rect) { intWidth->blockSignals(true); intHeight->blockSignals(true); doubleRatio->blockSignals(true); if (!chkWidth->isChecked()) intWidth->setValue(rect.width()); if (!chkHeight->isChecked()) intHeight->setValue(rect.height()); if (!chkRatio->isChecked() && !(rect.width() == 0 && rect.height() == 0)) { doubleRatio->setValue(fabs(rect.width()) / fabs(rect.height())); } intWidth->blockSignals(false); intHeight->blockSignals(false); doubleRatio->blockSignals(false); } diff --git a/krita/ui/tool/kis_rectangle_constraint_widget.h b/krita/ui/tool/kis_rectangle_constraint_widget.h index de40ccdf9f..fc803db6e4 100644 --- a/krita/ui/tool/kis_rectangle_constraint_widget.h +++ b/krita/ui/tool/kis_rectangle_constraint_widget.h @@ -1,42 +1,44 @@ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISRECTANGLECONSTRAINTWIDGET_H #define KISRECTANGLECONSTRAINTWIDGET_H #include "ui_wdgrectangleconstraints.h" -#include "kis_tool_rectangle_base.h" +#include + +class KisToolRectangleBase; class KRITAUI_EXPORT KisRectangleConstraintWidget : public QWidget, public Ui::WdgRectangleConstraints { Q_OBJECT public: KisRectangleConstraintWidget(QWidget *parentWidget, KisToolRectangleBase *tool); Q_SIGNALS: void constraintsChanged(bool forceRatio, bool forceWidth, bool forceHeight, float ratio, float width, float height); protected Q_SLOTS: void rectangleChanged(const QRectF &rect); void inputsChanged(); protected: KisToolRectangleBase* m_tool; Ui_WdgRectangleConstraints *m_widget; }; -#endif \ No newline at end of file +#endif diff --git a/krita/ui/tool/kis_resources_snapshot.cpp b/krita/ui/tool/kis_resources_snapshot.cpp index 24aea1d931..2dd71152c6 100644 --- a/krita/ui/tool/kis_resources_snapshot.cpp +++ b/krita/ui/tool/kis_resources_snapshot.cpp @@ -1,313 +1,312 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_resources_snapshot.h" #include #include #include #include "kis_paintop_preset.h" #include "kis_paintop_settings.h" #include "kis_paintop_registry.h" #include #include "KoPattern.h" #include "kis_canvas_resource_provider.h" #include "filter/kis_filter_configuration.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" #include "recorder/kis_recorded_paint_action.h" -#include "kis_default_bounds.h" #include "kis_selection.h" #include "kis_selection_mask.h" struct KisResourcesSnapshot::Private { Private() : currentPattern(0) , currentGradient(0) , currentGenerator(0) , compositeOp(0) { } KisImageWSP image; KisDefaultBoundsBaseSP bounds; KisPostExecutionUndoAdapter *undoAdapter; KoColor currentFgColor; KoColor currentBgColor; KoPattern *currentPattern; KoAbstractGradient *currentGradient; KisPaintOpPresetSP currentPaintOpPreset; KisNodeSP currentNode; qreal currentExposure; KisFilterConfiguration *currentGenerator; QPointF axesCenter; bool mirrorMaskHorizontal; bool mirrorMaskVertical; quint8 opacity; QString compositeOpId; const KoCompositeOp *compositeOp; KisPainter::StrokeStyle strokeStyle; KisPainter::FillStyle fillStyle; bool globalAlphaLock; qreal effectiveZoom; }; KisResourcesSnapshot::KisResourcesSnapshot(KisImageWSP image, KisNodeSP currentNode, KisPostExecutionUndoAdapter *undoAdapter, KoCanvasResourceManager *resourceManager, KisDefaultBoundsBaseSP bounds) : m_d(new Private()) { m_d->image = image; if (!bounds) { bounds = new KisDefaultBounds(m_d->image); } m_d->bounds = bounds; m_d->undoAdapter = undoAdapter; m_d->currentFgColor = resourceManager->resource(KoCanvasResourceManager::ForegroundColor).value(); m_d->currentBgColor = resourceManager->resource(KoCanvasResourceManager::BackgroundColor).value(); m_d->currentPattern = resourceManager->resource(KisCanvasResourceProvider::CurrentPattern).value(); m_d->currentGradient = resourceManager->resource(KisCanvasResourceProvider::CurrentGradient).value(); m_d->currentPaintOpPreset = resourceManager->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value(); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND KisPaintOpRegistry::instance()->preinitializePaintOpIfNeeded(m_d->currentPaintOpPreset); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ m_d->currentExposure = resourceManager->resource(KisCanvasResourceProvider::HdrExposure).toDouble(); m_d->currentGenerator = resourceManager->resource(KisCanvasResourceProvider::CurrentGeneratorConfiguration).value(); m_d->axesCenter = resourceManager->resource(KisCanvasResourceProvider::MirrorAxesCenter).toPointF(); if (m_d->axesCenter.isNull()){ QRect bounds = m_d->bounds->bounds(); m_d->axesCenter = QPointF(0.5 * bounds.width(), 0.5 * bounds.height()); } m_d->mirrorMaskHorizontal = resourceManager->resource(KisCanvasResourceProvider::MirrorHorizontal).toBool(); m_d->mirrorMaskVertical = resourceManager->resource(KisCanvasResourceProvider::MirrorVertical).toBool(); qreal normOpacity = resourceManager->resource(KisCanvasResourceProvider::Opacity).toDouble(); m_d->opacity = quint8(normOpacity * OPACITY_OPAQUE_U8); m_d->compositeOpId = resourceManager->resource(KisCanvasResourceProvider::CurrentCompositeOp).toString(); setCurrentNode(currentNode); /** * Fill and Stroke styles are not a part of the resource manager * so the tools should set them manually * TODO: port stroke and fill styles to be a part * of the resource manager */ m_d->strokeStyle = KisPainter::StrokeStyleBrush; m_d->fillStyle = KisPainter::FillStyleNone; m_d->globalAlphaLock = resourceManager->resource(KisCanvasResourceProvider::GlobalAlphaLock).toBool(); m_d->effectiveZoom = resourceManager->resource(KisCanvasResourceProvider::EffectiveZoom).toDouble(); } KisResourcesSnapshot::~KisResourcesSnapshot() { delete m_d; } void KisResourcesSnapshot::setupPainter(KisPainter* painter) { painter->setPaintColor(m_d->currentFgColor); painter->setBackgroundColor(m_d->currentBgColor); painter->setGenerator(m_d->currentGenerator); painter->setPattern(m_d->currentPattern); painter->setGradient(m_d->currentGradient); QBitArray lockflags = channelLockFlags(); if (lockflags.size() > 0) { painter->setChannelFlags(lockflags); } painter->setOpacity(m_d->opacity); painter->setCompositeOp(m_d->compositeOp); painter->setMirrorInformation(m_d->axesCenter, m_d->mirrorMaskHorizontal, m_d->mirrorMaskVertical); painter->setStrokeStyle(m_d->strokeStyle); painter->setFillStyle(m_d->fillStyle); /** * The paintOp should be initialized the last, because it may * ask the painter for some options while initialization */ painter->setPaintOpPreset(m_d->currentPaintOpPreset, m_d->currentNode, m_d->image); } void KisResourcesSnapshot::setupPaintAction(KisRecordedPaintAction *action) { action->setPaintOpPreset(m_d->currentPaintOpPreset); action->setPaintIncremental(!needsIndirectPainting()); action->setPaintColor(m_d->currentFgColor); action->setBackgroundColor(m_d->currentBgColor); action->setGenerator(m_d->currentGenerator); action->setGradient(m_d->currentGradient); action->setPattern(m_d->currentPattern); action->setOpacity(m_d->opacity / qreal(OPACITY_OPAQUE_U8)); action->setCompositeOp(m_d->compositeOp->id()); action->setStrokeStyle(m_d->strokeStyle); action->setFillStyle(m_d->fillStyle); } KisPostExecutionUndoAdapter* KisResourcesSnapshot::postExecutionUndoAdapter() const { return m_d->undoAdapter; } void KisResourcesSnapshot::setCurrentNode(KisNodeSP node) { m_d->currentNode = node; KisPaintDeviceSP device; if(m_d->currentNode && (device = m_d->currentNode->paintDevice())) { m_d->compositeOp = device->colorSpace()->compositeOp(m_d->compositeOpId); if(!m_d->compositeOp) { m_d->compositeOp = device->colorSpace()->compositeOp(COMPOSITE_OVER); } } } void KisResourcesSnapshot::setStrokeStyle(KisPainter::StrokeStyle strokeStyle) { m_d->strokeStyle = strokeStyle; } void KisResourcesSnapshot::setFillStyle(KisPainter::FillStyle fillStyle) { m_d->fillStyle = fillStyle; } KisNodeSP KisResourcesSnapshot::currentNode() const { return m_d->currentNode; } KisImageWSP KisResourcesSnapshot::image() const { return m_d->image; } bool KisResourcesSnapshot::needsIndirectPainting() const { return !m_d->currentPaintOpPreset->settings()->paintIncremental(); } QString KisResourcesSnapshot::indirectPaintingCompositeOp() const { return m_d->currentPaintOpPreset->settings()->indirectPaintingCompositeOp(); } KisSelectionSP KisResourcesSnapshot::activeSelection() const { /** * It is possible to have/use the snapshot without the image. Such * usecase is present for example in the scratchpad. */ KisSelectionSP selection = m_d->image ? m_d->image->globalSelection() : 0; KisLayerSP layer = dynamic_cast(m_d->currentNode.data()); KisSelectionMaskSP mask; if((layer = dynamic_cast(m_d->currentNode.data()))) { selection = layer->selection(); } else if ((mask = dynamic_cast(m_d->currentNode.data())) && mask->selection() == selection) { selection = 0; } return selection; } bool KisResourcesSnapshot::needsAirbrushing() const { return m_d->currentPaintOpPreset->settings()->isAirbrushing(); } int KisResourcesSnapshot::airbrushingRate() const { return m_d->currentPaintOpPreset->settings()->rate(); } void KisResourcesSnapshot::setOpacity(qreal opacity) { m_d->opacity = opacity * OPACITY_OPAQUE_U8; } quint8 KisResourcesSnapshot::opacity() const { return m_d->opacity; } const KoCompositeOp* KisResourcesSnapshot::compositeOp() const { return m_d->compositeOp; } KoPattern* KisResourcesSnapshot::currentPattern() const { return m_d->currentPattern; } KoColor KisResourcesSnapshot::currentFgColor() const { return m_d->currentFgColor; } KoColor KisResourcesSnapshot::currentBgColor() const { return m_d->currentBgColor; } KisPaintOpPresetSP KisResourcesSnapshot::currentPaintOpPreset() const { return m_d->currentPaintOpPreset; } QBitArray KisResourcesSnapshot::channelLockFlags() const { QBitArray channelFlags; KisPaintLayer *paintLayer; if ((paintLayer = dynamic_cast(m_d->currentNode.data()))) { channelFlags = paintLayer->channelLockFlags(); if (m_d->globalAlphaLock) { if (channelFlags.isEmpty()) { channelFlags = paintLayer->colorSpace()->channelFlags(true, true); } channelFlags &= paintLayer->colorSpace()->channelFlags(true, false); } } return channelFlags; } qreal KisResourcesSnapshot::effectiveZoom() const { return m_d->effectiveZoom; } diff --git a/krita/ui/tool/kis_tool_freehand.h b/krita/ui/tool/kis_tool_freehand.h index 36a1578441..c890fe68a6 100644 --- a/krita/ui/tool/kis_tool_freehand.h +++ b/krita/ui/tool/kis_tool_freehand.h @@ -1,133 +1,132 @@ /* * Copyright (c) 2003-2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_FREEHAND_H_ #define KIS_TOOL_FREEHAND_H_ #include "kis_types.h" #include "kis_tool_paint.h" #include "kis_paint_information.h" -#include "kis_resources_snapshot.h" #include "kis_paintop_settings.h" #include "kis_distance_information.h" #include "kis_smoothing_options.h" #include "kritaui_export.h" class KoPointerEvent; class KoCanvasBase; class KisPaintingInformationBuilder; class KisToolFreehandHelper; class KisRecordingAdapter; class KRITAUI_EXPORT KisToolFreehand : public KisToolPaint { Q_OBJECT public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW KisToolFreehand(KoCanvasBase * canvas, const QCursor & cursor, const KUndo2MagicString &transactionText); virtual ~KisToolFreehand(); virtual int flags() const; public Q_SLOTS: virtual void activate(ToolActivation toolActivation, const QSet &shapes); void deactivate(); protected: bool tryPickByPaintOp(KoPointerEvent *event, AlternateAction action); bool primaryActionSupportsHiResEvents() const; void beginPrimaryAction(KoPointerEvent *event); void continuePrimaryAction(KoPointerEvent *event); void endPrimaryAction(KoPointerEvent *event); void activateAlternateAction(AlternateAction action); void deactivateAlternateAction(AlternateAction action); void beginAlternateAction(KoPointerEvent *event, AlternateAction action); void continueAlternateAction(KoPointerEvent *event, AlternateAction action); void endAlternateAction(KoPointerEvent *event, AlternateAction action); virtual bool wantsAutoScroll() const; virtual void initStroke(KoPointerEvent *event); virtual void doStroke(KoPointerEvent *event); virtual void endStroke(); virtual QPainterPath getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode); KisPaintingInformationBuilder* paintingInformationBuilder() const; KisRecordingAdapter* recordingAdapter() const; void resetHelper(KisToolFreehandHelper *helper); protected Q_SLOTS: void explicitUpdateOutline(); virtual void resetCursorStyle(); void setAssistant(bool assistant); void setOnlyOneAssistantSnap(bool assistant); private: friend class KisToolFreehandPaintingInformationBuilder; /** * Adjusts a coordinates according to a KisPaintingAssitant, * if available. */ QPointF adjustPosition(const QPointF& point, const QPointF& strokeBegin); /** * Calculates a coefficient for KisPaintInformation * according to perspective grid values */ qreal calculatePerspective(const QPointF &documentPoint); protected: friend class KisViewManager; friend class KisView; friend class KisSketchView; KisSmoothingOptionsSP smoothingOptions() const; bool m_assistant; double m_magnetism; bool m_only_one_assistant; private: KisPaintingInformationBuilder *m_infoBuilder; KisToolFreehandHelper *m_helper; KisRecordingAdapter *m_recordingAdapter; QPointF m_initialGestureDocPoint; QPointF m_lastDocumentPoint; QPoint m_initialGestureGlobalPoint; }; #endif // KIS_TOOL_FREEHAND_H_ diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis_tool_freehand_helper.cpp index e28cce0496..80c64a5f01 100644 --- a/krita/ui/tool/kis_tool_freehand_helper.cpp +++ b/krita/ui/tool/kis_tool_freehand_helper.cpp @@ -1,845 +1,844 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_tool_freehand_helper.h" #include #include #include #include #include #include "kis_distance_information.h" #include "kis_painting_information_builder.h" #include "kis_recording_adapter.h" #include "kis_image.h" #include "kis_painter.h" -#include "kis_smoothing_options.h" #include "kis_paintop_preset.h" #include "kis_paintop_utils.h" #include "kis_update_time_monitor.h" #include //#define DEBUG_BEZIER_CURVES struct KisToolFreehandHelper::Private { KisPaintingInformationBuilder *infoBuilder; KisRecordingAdapter *recordingAdapter; KisStrokesFacade *strokesFacade; KUndo2MagicString transactionText; bool haveTangent; QPointF previousTangent; bool hasPaintAtLeastOnce; QTime strokeTime; QTimer strokeTimeoutTimer; QVector painterInfos; KisResourcesSnapshotSP resources; KisStrokeId strokeId; KisPaintInformation previousPaintInformation; KisPaintInformation olderPaintInformation; KisSmoothingOptionsSP smoothingOptions; QTimer airbrushingTimer; QList history; QList distanceHistory; KisPaintOpUtils::PositionHistory lastOutlinePos; // Stabilizer data QQueue stabilizerDeque; KisPaintInformation stabilizerLastPaintInfo; QTimer stabilizerPollTimer; int canvasRotation; bool canvasMirroredH; KisPaintInformation getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo); qreal effectiveSmoothnessDistance() const; }; KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText, KisRecordingAdapter *recordingAdapter) : m_d(new Private()) { m_d->infoBuilder = infoBuilder; m_d->recordingAdapter = recordingAdapter; m_d->transactionText = transactionText; m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions()); m_d->canvasRotation = 0; m_d->strokeTimeoutTimer.setSingleShot(true); connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke())); connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing())); connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint())); } KisToolFreehandHelper::~KisToolFreehandHelper() { delete m_d; } void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions) { m_d->smoothingOptions = smoothingOptions; } KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const { return m_d->smoothingOptions; } QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettings *globalSettings, KisPaintOpSettings::OutlineMode mode) const { const KisPaintOpSettings *settings = globalSettings; KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event); info.setCanvasRotation(m_d->canvasRotation); info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0); if (!m_d->painterInfos.isEmpty()) { settings = m_d->resources->currentPaintOpPreset()->settings(); info = m_d->previousPaintInformation; distanceInfo = *m_d->painterInfos.first()->dragDistance; } KisPaintInformation::DistanceInformationRegistrar registrar = info.registerDistanceInformation(&distanceInfo); QPainterPath outline = settings->brushOutline(info, mode); if (m_d->resources && m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER && m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); outline.addEllipse(info.pos(), R, R); } return outline; } void KisToolFreehandHelper::initPaint(KoPointerEvent *event, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { KisPaintInformation pi = m_d->infoBuilder->startStroke(event, elapsedStrokeTime()); initPaintImpl(pi, resourceManager, image, currentNode, strokesFacade, undoAdapter, overrideNode, bounds); } bool KisToolFreehandHelper::isRunning() const { return m_d->strokeId; } void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode, KisDefaultBoundsBaseSP bounds) { Q_UNUSED(overrideNode); m_d->strokesFacade = strokesFacade; m_d->haveTangent = false; m_d->previousTangent = QPointF(); m_d->hasPaintAtLeastOnce = false; m_d->strokeTime.start(); m_d->previousPaintInformation = previousPaintInformation; createPainters(m_d->painterInfos, m_d->previousPaintInformation.pos(), m_d->previousPaintInformation.currentTime()); m_d->resources = new KisResourcesSnapshot(image, currentNode, undoAdapter, resourceManager, bounds); if(overrideNode) { m_d->resources->setCurrentNode(overrideNode); } if(m_d->recordingAdapter) { m_d->recordingAdapter->startStroke(image, m_d->resources); } KisStrokeStrategy *stroke = new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(), m_d->resources->indirectPaintingCompositeOp(), m_d->resources, m_d->painterInfos, m_d->transactionText); m_d->strokeId = m_d->strokesFacade->startStroke(stroke); m_d->history.clear(); m_d->distanceHistory.clear(); if(m_d->resources->needsAirbrushing()) { m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate()); m_d->airbrushingTimer.start(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerStart(m_d->previousPaintInformation); } } void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2) { if (tangent1.isNull() || tangent2.isNull()) return; const qreal maxSanePoint = 1e6; QPointF controlTarget1; QPointF controlTarget2; // Shows the direction in which control points go QPointF controlDirection1 = pi1.pos() + tangent1; QPointF controlDirection2 = pi2.pos() - tangent2; // Lines in the direction of the control points QLineF line1(pi1.pos(), controlDirection1); QLineF line2(pi2.pos(), controlDirection2); // Lines to check whether the control points lay on the opposite // side of the line QLineF line3(controlDirection1, controlDirection2); QLineF line4(pi1.pos(), pi2.pos()); QPointF intersection; if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) { qreal controlLength = line4.length() / 2; line1.setLength(controlLength); line2.setLength(controlLength); controlTarget1 = line1.p2(); controlTarget2 = line2.p2(); } else { QLineF::IntersectType type = line1.intersect(line2, &intersection); if (type == QLineF::NoIntersection || intersection.manhattanLength() > maxSanePoint) { intersection = 0.5 * (pi1.pos() + pi2.pos()); // dbgKrita << "WARINING: there is no intersection point " // << "in the basic smoothing algoriths"; } controlTarget1 = intersection; controlTarget2 = intersection; } // shows how near to the controlTarget the value raises qreal coeff = 0.8; qreal velocity1 = QLineF(QPointF(), tangent1).length(); qreal velocity2 = QLineF(QPointF(), tangent2).length(); if (velocity1 == 0.0 || velocity2 == 0.0) { velocity1 = 1e-6; velocity2 = 1e-6; warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2); } qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1); // the controls should not differ more than 50% similarity = qMax(similarity, qreal(0.5)); // when the controls are symmetric, their size should be smaller // to avoid corner-like curves coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8)); Q_ASSERT(coeff > 0); QPointF control1; QPointF control2; if (velocity1 > velocity2) { control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; coeff *= similarity; control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; } else { control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2; coeff *= similarity; control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1; } paintBezierCurve(pi1, control1, control2, pi2); } qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const { const qreal effectiveSmoothnessDistance = !smoothingOptions->useScalableDistance() ? smoothingOptions->smoothnessDistance() : smoothingOptions->smoothnessDistance() / resources->effectiveZoom(); return effectiveSmoothnessDistance; } void KisToolFreehandHelper::paint(KoPointerEvent *event) { KisPaintInformation info = m_d->infoBuilder->continueStroke(event, elapsedStrokeTime()); info.setCanvasRotation( m_d->canvasRotation ); info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH ); KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos()); /** * Smooth the coordinates out using the history and the * distance. This is a heavily modified version of an algo used in * Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and * http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main * differences are: * * 1) It uses 'distance' instead of 'velocity', since time * measurements are too unstable in realworld environment * * 2) There is no 'Quality' parameter, since the number of samples * is calculated automatically * * 3) 'Tail Aggressiveness' is used for controling the end of the * stroke * * 4) The formila is a little bit different: 'Distance' parameter * stands for $3 \Sigma$ */ if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING && m_d->smoothingOptions->smoothnessDistance() > 0.0) { { // initialize current distance QPointF prevPos; if (!m_d->history.isEmpty()) { const KisPaintInformation &prevPi = m_d->history.last(); prevPos = prevPi.pos(); } else { prevPos = m_d->previousPaintInformation.pos(); } qreal currentDistance = QVector2D(info.pos() - prevPos).length(); m_d->distanceHistory.append(currentDistance); } m_d->history.append(info); qreal x = 0.0; qreal y = 0.0; if (m_d->history.size() > 3) { const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma); qreal gaussianWeight2 = sigma * sigma; qreal distanceSum = 0.0; qreal scaleSum = 0.0; qreal pressure = 0.0; qreal baseRate = 0.0; Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size()); for (int i = m_d->history.size() - 1; i >= 0; i--) { qreal rate = 0.0; const KisPaintInformation nextInfo = m_d->history.at(i); double distance = m_d->distanceHistory.at(i); Q_ASSERT(distance >= 0.0); qreal pressureGrad = 0.0; if (i < m_d->history.size() - 1) { pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure(); const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness(); if (pressureGrad > 0.0 ) { pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure()); distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region } } if (gaussianWeight2 != 0.0) { distanceSum += distance; rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2)); } if (m_d->history.size() - i == 1) { baseRate = rate; } else if (baseRate / rate > 100) { break; } scaleSum += rate; x += rate * nextInfo.pos().x(); y += rate * nextInfo.pos().y(); if (m_d->smoothingOptions->smoothPressure()) { pressure += rate * nextInfo.pressure(); } } if (scaleSum != 0.0) { x /= scaleSum; y /= scaleSum; if (m_d->smoothingOptions->smoothPressure()) { pressure /= scaleSum; } } if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) { info.setPos(QPointF(x, y)); if (m_d->smoothingOptions->smoothPressure()) { info.setPressure(pressure); } m_d->history.last() = info; } } } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING || m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING) { // Now paint between the coordinates, using the bezier curve interpolation if (!m_d->haveTangent) { m_d->haveTangent = true; m_d->previousTangent = (info.pos() - m_d->previousPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime()); } else { QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) / qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime()); paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); m_d->previousTangent = newTangent; } m_d->olderPaintInformation = m_d->previousPaintInformation; m_d->strokeTimeoutTimer.start(100); } else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){ paintLine(m_d->previousPaintInformation, info); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { m_d->stabilizerLastPaintInfo = info; } else { m_d->previousPaintInformation = info; } if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.start(); } } void KisToolFreehandHelper::endPaint() { if (!m_d->hasPaintAtLeastOnce) { paintAt(m_d->previousPaintInformation); } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) { finishStroke(); } m_d->strokeTimeoutTimer.stop(); if(m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) { stabilizerEnd(); } /** * There might be some timer events still pending, so * we should cancel them. Use this flag for the purpose. * Please note that we are not in MT here, so no mutex * is needed */ m_d->painterInfos.clear(); m_d->strokesFacade->endStroke(m_d->strokeId); m_d->strokeId.clear(); if(m_d->recordingAdapter) { m_d->recordingAdapter->endStroke(); } } void KisToolFreehandHelper::cancelPaint() { if (!m_d->strokeId) return; m_d->strokeTimeoutTimer.stop(); if (m_d->airbrushingTimer.isActive()) { m_d->airbrushingTimer.stop(); } if (m_d->stabilizerPollTimer.isActive()) { m_d->stabilizerPollTimer.stop(); } // see a comment in endPaint() m_d->painterInfos.clear(); m_d->strokesFacade->cancelStroke(m_d->strokeId); m_d->strokeId.clear(); if(m_d->recordingAdapter) { //FIXME: not implemented //m_d->recordingAdapter->cancelStroke(); } } int KisToolFreehandHelper::elapsedStrokeTime() const { return m_d->strokeTime.elapsed(); } void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo) { // FIXME: Ugly hack, this is no a "distance" in any way int sampleSize = qRound(m_d->effectiveSmoothnessDistance()); sampleSize = qMax(3, sampleSize); // Fill the deque with the current value repeated until filling the sample m_d->stabilizerDeque.clear(); for (int i = sampleSize; i > 0; i--) { m_d->stabilizerDeque.enqueue(firstPaintInfo); } m_d->stabilizerLastPaintInfo = firstPaintInfo; // Poll and draw each millisecond m_d->stabilizerPollTimer.setInterval(1); m_d->stabilizerPollTimer.start(); } KisPaintInformation KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue &queue, const KisPaintInformation &lastPaintInfo) { KisPaintInformation result(lastPaintInfo); if (queue.size() > 1) { QQueue::const_iterator it = queue.constBegin(); QQueue::const_iterator end = queue.constEnd(); /** * The first point is going to be overridden by lastPaintInfo, skip it. */ it++; int i = 2; if (smoothingOptions->stabilizeSensors()) { while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result = KisPaintInformation::mix(k, *it, result); it++; i++; } } else{ while (it != end) { qreal k = qreal(i - 1) / i; // coeff for uniform averaging result = KisPaintInformation::mixOnlyPosition(k, *it, result); it++; i++; } } } return result; } void KisToolFreehandHelper::stabilizerPollAndPaint() { KisPaintInformation newInfo = m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, m_d->stabilizerLastPaintInfo); bool canPaint = true; if (m_d->smoothingOptions->useDelayDistance()) { const qreal R = m_d->smoothingOptions->delayDistance() / m_d->resources->effectiveZoom(); QPointF diff = m_d->stabilizerLastPaintInfo.pos() - m_d->previousPaintInformation.pos(); qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y())); canPaint = dx > R; } if (canPaint) { paintLine(m_d->previousPaintInformation, newInfo); m_d->previousPaintInformation = newInfo; // Push the new entry through the queue m_d->stabilizerDeque.dequeue(); m_d->stabilizerDeque.enqueue(m_d->stabilizerLastPaintInfo); emit requestExplicitUpdateOutline(); } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) { QQueue::iterator it = m_d->stabilizerDeque.begin(); QQueue::iterator end = m_d->stabilizerDeque.end(); while (it != end) { *it = m_d->previousPaintInformation; ++it; } } } void KisToolFreehandHelper::stabilizerEnd() { // FIXME: Ugly hack, this is no a "distance" in any way int sampleSize = m_d->smoothingOptions->smoothnessDistance(); assert(sampleSize > 0); // Stop the timer m_d->stabilizerPollTimer.stop(); // Finish the line for (int i = sampleSize; i > 0; i--) { // In each iteration we add the latest paint info and delete the oldest // After `sampleSize` iterations the deque will be filled with the latest // value and we will have reached the end point. if (m_d->smoothingOptions->finishStabilizedCurve()) { stabilizerPollAndPaint(); } } } const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const { return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0; } void KisToolFreehandHelper::finishStroke() { if (m_d->haveTangent) { m_d->haveTangent = false; QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) / (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime()); paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation, m_d->previousTangent, newTangent); } } void KisToolFreehandHelper::doAirbrushing() { if(!m_d->painterInfos.isEmpty()) { paintAt(m_d->previousPaintInformation); } } void KisToolFreehandHelper::paintAt(PainterInfo *painterInfo, const KisPaintInformation &pi) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), painterInfo, pi)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addPoint(pi); } } void KisToolFreehandHelper::paintLine(PainterInfo *painterInfo, const KisPaintInformation &pi1, const KisPaintInformation &pi2) { m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), painterInfo, pi1, pi2)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addLine(pi1, pi2); } } void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { #ifdef DEBUG_BEZIER_CURVES KisPaintInformation tpi1; KisPaintInformation tpi2; tpi1 = pi1; tpi2 = pi2; tpi1.setPressure(0.3); tpi2.setPressure(0.3); paintLine(tpi1, tpi2); tpi1.setPressure(0.6); tpi2.setPressure(0.3); tpi1.setPos(pi1.pos()); tpi2.setPos(control1); paintLine(tpi1, tpi2); tpi1.setPos(pi2.pos()); tpi2.setPos(control2); paintLine(tpi1, tpi2); #endif m_d->hasPaintAtLeastOnce = true; m_d->strokesFacade->addJob(m_d->strokeId, new FreehandStrokeStrategy::Data(m_d->resources->currentNode(), painterInfo, pi1, control1, control2, pi2)); if(m_d->recordingAdapter) { m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2); } } void KisToolFreehandHelper::createPainters(QVector &painterInfos, const QPointF &lastPosition, int lastTime) { painterInfos << new PainterInfo(new KisPainter(), new KisDistanceInformation(lastPosition, lastTime)); } void KisToolFreehandHelper::paintAt(const QVector &painterInfos, const KisPaintInformation &pi) { paintAt(painterInfos.first(), pi); } void KisToolFreehandHelper::paintLine(const QVector &painterInfos, const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(painterInfos.first(), pi1, pi2); } void KisToolFreehandHelper::paintBezierCurve(const QVector &painterInfos, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { paintBezierCurve(painterInfos.first(), pi1, control1, control2, pi2); } void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi) { paintAt(m_d->painterInfos, pi); } void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2) { paintLine(m_d->painterInfos, pi1, pi2); } void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2) { paintBezierCurve(m_d->painterInfos, pi1, control1, control2, pi2); } int KisToolFreehandHelper::canvasRotation() { return m_d->canvasRotation; } void KisToolFreehandHelper::setCanvasRotation(int rotation) { m_d->canvasRotation = rotation; } bool KisToolFreehandHelper::canvasMirroredH() { return m_d->canvasMirroredH; } void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored) { m_d->canvasMirroredH = mirrored; } diff --git a/krita/ui/tool/kis_tool_freehand_helper.h b/krita/ui/tool/kis_tool_freehand_helper.h index b49b306c58..79518cf2da 100644 --- a/krita/ui/tool/kis_tool_freehand_helper.h +++ b/krita/ui/tool/kis_tool_freehand_helper.h @@ -1,169 +1,169 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_TOOL_FREEHAND_HELPER_H #define __KIS_TOOL_FREEHAND_HELPER_H #include #include "kis_types.h" #include "kritaui_export.h" #include "kis_paint_information.h" -#include "strokes/freehand_stroke.h" #include "kis_default_bounds.h" #include "kis_paintop_settings.h" #include "kis_smoothing_options.h" +#include "strokes/freehand_stroke.h" class KoPointerEvent; class KoCanvasResourceManager; class KisPaintingInformationBuilder; class KisRecordingAdapter; class KisStrokesFacade; class KisPostExecutionUndoAdapter; class KisPaintOp; class KRITAUI_EXPORT KisToolFreehandHelper : public QObject { Q_OBJECT protected: typedef FreehandStrokeStrategy::PainterInfo PainterInfo; public: KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder, const KUndo2MagicString &transactionText = KUndo2MagicString(), KisRecordingAdapter *recordingAdapter = 0); ~KisToolFreehandHelper(); void setSmoothness(KisSmoothingOptionsSP smoothingOptions); KisSmoothingOptionsSP smoothingOptions() const; bool isRunning() const; void initPaint(KoPointerEvent *event, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode = 0, KisDefaultBoundsBaseSP bounds = 0); void paint(KoPointerEvent *event); void endPaint(); const KisPaintOp* currentPaintOp() const; QPainterPath paintOpOutline(const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettings *globalSettings, KisPaintOpSettings::OutlineMode mode) const; int canvasRotation(); void setCanvasRotation(int rotation = 0); bool canvasMirroredH(); void setCanvasHorizontalMirrorState (bool mirrored = false); Q_SIGNALS: /** * The signal is emitted when the outline should be updated * explicitly by the tool. Used by Stabilizer option, because it * paints on internal timer events instead of the on every paint() * event */ void requestExplicitUpdateOutline(); protected: void cancelPaint(); int elapsedStrokeTime() const; void initPaintImpl(const KisPaintInformation &previousPaintInformation, KoCanvasResourceManager *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisPostExecutionUndoAdapter *undoAdapter, KisNodeSP overrideNode = 0, KisDefaultBoundsBaseSP bounds = 0); protected: virtual void createPainters(QVector &painterInfos, const QPointF &lastPosition, int lastTime); // to be overridden in derived classes to add painting with // multiple painters virtual void paintAt(const QVector &painterInfos, const KisPaintInformation &pi); virtual void paintLine(const QVector &painterInfos, const KisPaintInformation &pi1, const KisPaintInformation &pi2); virtual void paintBezierCurve(const QVector &painterInfos, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2); // lo-level methods for painting primitives void paintAt(PainterInfo *painterInfo, const KisPaintInformation &pi); void paintLine(PainterInfo *painterInfo, const KisPaintInformation &pi1, const KisPaintInformation &pi2); void paintBezierCurve(PainterInfo *painterInfo, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2); // hi-level methods for painting primitives void paintAt(const KisPaintInformation &pi); void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2); void paintBezierCurve(const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2); private: void paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2); void stabilizerStart(KisPaintInformation firstPaintInfo); void stabilizerEnd(); private Q_SLOTS: void finishStroke(); void doAirbrushing(); void stabilizerPollAndPaint(); private: struct Private; Private * const m_d; }; #endif /* __KIS_TOOL_FREEHAND_HELPER_H */ diff --git a/krita/ui/tool/kis_tool_paint.h b/krita/ui/tool/kis_tool_paint.h index 30c6413e39..9b53d8e19f 100644 --- a/krita/ui/tool/kis_tool_paint.h +++ b/krita/ui/tool/kis_tool_paint.h @@ -1,215 +1,206 @@ /* * Copyright (c) 2003 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_PAINT_H_ #define KIS_TOOL_PAINT_H_ #include #include #include -#include #include -#include -#include -#include #include #include #include #include #include #include #include #include #include #include #include "kis_tool.h" #include -class QEvent; -class QKeyEvent; -class QPaintEvent; class QGridLayout; -class QLabel; -class QPoint; class KoCompositeOp; class KoCanvasBase; // wacom const static int LEVEL_OF_PRESSURE_RESOLUTION = 1024; class KRITAUI_EXPORT KisToolPaint : public KisTool { Q_OBJECT public: KisToolPaint(KoCanvasBase * canvas, const QCursor & cursor); virtual ~KisToolPaint(); virtual int flags() const; virtual void mousePressEvent(KoPointerEvent *event); virtual void mouseReleaseEvent(KoPointerEvent *event); virtual void mouseMoveEvent(KoPointerEvent *event); protected: void setMode(ToolMode mode); virtual void canvasResourceChanged(int key, const QVariant & v); virtual void paint(QPainter& gc, const KoViewConverter &converter); virtual void activatePrimaryAction(); virtual void deactivatePrimaryAction(); virtual void activateAlternateAction(AlternateAction action); virtual void deactivateAlternateAction(AlternateAction action); virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action); virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event); /** If the paint tool support outline like brushes, set to true. * If not (e.g. gradient tool), set to false. Default is false. */ void setSupportOutline(bool supportOutline) { m_supportOutline = supportOutline; } virtual QPainterPath getOutlinePath(const QPointF &documentPos, const KoPointerEvent *event, KisPaintOpSettings::OutlineMode outlineMode); protected: bool isOutlineEnabled() const; void setOutlineEnabled(bool enabled); bool pickColor(const QPointF &documentPixel, AlternateAction action); /// Add the tool-specific layout to the default option widget layout. void addOptionWidgetLayout(QLayout *layout); /// Add a widget and a label to the current option widget layout. virtual void addOptionWidgetOption(QWidget *control, QWidget *label = 0); void showControl(QWidget *control, bool value); void enableControl(QWidget *control, bool value); virtual QWidget * createOptionWidget(); /** * Quick help is a short help text about the way the tool functions. */ virtual QString quickHelp() const { return QString(); } virtual void setupPaintAction(KisRecordedPaintAction* action); qreal pressureToCurve(qreal pressure){ return m_pressureSamples.at( qRound(pressure * LEVEL_OF_PRESSURE_RESOLUTION) ); } enum NodePaintAbility { NONE, PAINT, VECTOR }; /// Checks if and how the tool can paint on the current node NodePaintAbility nodePaintAbility(); const KoCompositeOp* compositeOp(); public Q_SLOTS: virtual void activate(ToolActivation toolActivation, const QSet &shapes); virtual void deactivate(); private Q_SLOTS: void slotPopupQuickHelp(); void increaseBrushSize(); void decreaseBrushSize(); void activatePickColorDelayed(); protected Q_SLOTS: void updateTabletPressureSamples(); protected: quint8 m_opacity; bool m_paintOutline; QVector m_pressureSamples; QPointF m_outlineDocPoint; QPainterPath m_currentOutline; QRectF m_oldOutlineRect; bool m_showColorPreview; QRectF m_oldColorPreviewRect; QRectF m_oldColorPreviewUpdateRect; QColor m_colorPreviewCurrentColor; bool m_colorPreviewShowComparePlate; QColor m_colorPreviewBaseColor; private: QPainterPath tryFixBrushOutline(const QPainterPath &originalOutline); void setOpacity(qreal opacity); void activatePickColor(AlternateAction action); void deactivatePickColor(AlternateAction action); void pickColorWasOverridden(); int colorPreviewResourceId(AlternateAction action); QRectF colorPreviewDocRect(const QPointF &outlineDocPoint); private: bool m_specialHoverModifier; QGridLayout *m_optionsWidgetLayout; bool m_supportOutline; /** * Used as a switch for pickColor */ // used to skip some of the tablet events and don't update the colour that often QTimer m_colorPickerDelayTimer; AlternateAction delayedAction; bool m_isOutlineEnabled; std::vector m_standardBrushSizes; Q_SIGNALS: void sigPaintingFinished(); }; #endif // KIS_TOOL_PAINT_H_ diff --git a/krita/ui/tool/strokes/freehand_stroke.h b/krita/ui/tool/strokes/freehand_stroke.h index 99b9ea3a6c..3ddf25ecca 100644 --- a/krita/ui/tool/strokes/freehand_stroke.h +++ b/krita/ui/tool/strokes/freehand_stroke.h @@ -1,128 +1,127 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __FREEHAND_STROKE_H #define __FREEHAND_STROKE_H #include "kritaui_export.h" #include "kis_types.h" #include "kis_node.h" #include "kis_painter_based_stroke_strategy.h" #include "kis_distance_information.h" #include "kis_paint_information.h" -class KisPainter; class KRITAUI_EXPORT FreehandStrokeStrategy : public KisPainterBasedStrokeStrategy { public: class Data : public KisStrokeJobData { public: enum DabType { POINT, LINE, CURVE, POLYLINE, POLYGON, RECT, ELLIPSE, PAINTER_PATH }; Data(KisNodeSP _node, PainterInfo *_painterInfo, const KisPaintInformation &_pi) : node(_node), painterInfo(_painterInfo), type(POINT), pi1(_pi) {} Data(KisNodeSP _node, PainterInfo *_painterInfo, const KisPaintInformation &_pi1, const KisPaintInformation &_pi2) : node(_node), painterInfo(_painterInfo), type(LINE), pi1(_pi1), pi2(_pi2) {} Data(KisNodeSP _node, PainterInfo *_painterInfo, const KisPaintInformation &_pi1, const QPointF &_control1, const QPointF &_control2, const KisPaintInformation &_pi2) : node(_node), painterInfo(_painterInfo), type(CURVE), pi1(_pi1), pi2(_pi2), control1(_control1), control2(_control2) {} Data(KisNodeSP _node, PainterInfo *_painterInfo, DabType _type, const vQPointF &_points) : node(_node), painterInfo(_painterInfo), type(_type), points(_points) {} Data(KisNodeSP _node, PainterInfo *_painterInfo, DabType _type, const QRectF &_rect) : node(_node), painterInfo(_painterInfo), type(_type), rect(_rect) {} Data(KisNodeSP _node, PainterInfo *_painterInfo, DabType _type, const QPainterPath &_path) : node(_node), painterInfo(_painterInfo), type(_type), path(_path) {} KisNodeSP node; PainterInfo *painterInfo; DabType type; KisPaintInformation pi1; KisPaintInformation pi2; QPointF control1; QPointF control2; vQPointF points; QRectF rect; QPainterPath path; }; public: FreehandStrokeStrategy(bool needsIndirectPainting, const QString &indirectPaintingCompositeOp, KisResourcesSnapshotSP resources, PainterInfo *painterInfo, const KUndo2MagicString &name); FreehandStrokeStrategy(bool needsIndirectPainting, const QString &indirectPaintingCompositeOp, KisResourcesSnapshotSP resources, QVector painterInfos, const KUndo2MagicString &name); ~FreehandStrokeStrategy(); void doStrokeCallback(KisStrokeJobData *data); private: void init(bool needsIndirectPainting, const QString &indirectPaintingCompositeOp); }; #endif /* __FREEHAND_STROKE_H */ diff --git a/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp b/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp index 5a4238b6da..6cdc0860d5 100644 --- a/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp +++ b/krita/ui/tool/strokes/kis_painter_based_stroke_strategy.cpp @@ -1,211 +1,210 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_painter_based_stroke_strategy.h" #include #include #include "kis_painter.h" #include "kis_paint_device.h" #include "kis_paint_layer.h" -#include "kis_selection.h" #include "kis_transaction.h" #include "kis_image.h" #include "kis_distance_information.h" KisPainterBasedStrokeStrategy::PainterInfo::PainterInfo(KisPainter *_painter, KisDistanceInformation *_dragDistance) : painter(_painter), dragDistance(_dragDistance) { } KisPainterBasedStrokeStrategy::PainterInfo::~PainterInfo() { delete(painter); delete(dragDistance); } KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id, const KUndo2MagicString &name, KisResourcesSnapshotSP resources, QVector painterInfos,bool useMergeID) : KisSimpleStrokeStrategy(id, name), m_resources(resources), m_painterInfos(painterInfos), m_transaction(0), m_useMergeID(useMergeID) { init(); } KisPainterBasedStrokeStrategy::KisPainterBasedStrokeStrategy(const QString &id, const KUndo2MagicString &name, KisResourcesSnapshotSP resources, PainterInfo *painterInfo,bool useMergeID) : KisSimpleStrokeStrategy(id, name), m_resources(resources), m_painterInfos(QVector() << painterInfo), m_transaction(0), m_useMergeID(useMergeID) { init(); } void KisPainterBasedStrokeStrategy::init() { enableJob(KisSimpleStrokeStrategy::JOB_INIT); enableJob(KisSimpleStrokeStrategy::JOB_FINISH); enableJob(KisSimpleStrokeStrategy::JOB_CANCEL, true, KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); } KisPaintDeviceSP KisPainterBasedStrokeStrategy::targetDevice() { return m_targetDevice; } KisSelectionSP KisPainterBasedStrokeStrategy::activeSelection() { return m_activeSelection; } void KisPainterBasedStrokeStrategy::initPainters(KisPaintDeviceSP targetDevice, KisSelectionSP selection, bool hasIndirectPainting, const QString &indirectPaintingCompositeOp) { foreach(PainterInfo *info, m_painterInfos) { KisPainter *painter = info->painter; painter->begin(targetDevice, !hasIndirectPainting ? selection : 0); m_resources->setupPainter(painter); if(hasIndirectPainting) { painter->setCompositeOp(targetDevice->colorSpace()->compositeOp(indirectPaintingCompositeOp)); painter->setOpacity(OPACITY_OPAQUE_U8); painter->setChannelFlags(QBitArray()); } } } void KisPainterBasedStrokeStrategy::deletePainters() { foreach(PainterInfo *info, m_painterInfos) { delete info; } m_painterInfos.clear(); } void KisPainterBasedStrokeStrategy::initStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisPaintDeviceSP paintDevice = node->paintDevice(); KisPaintDeviceSP targetDevice = paintDevice; bool hasIndirectPainting = needsIndirectPainting(); KisSelectionSP selection = m_resources->activeSelection(); if (hasIndirectPainting) { KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if (indirect) { targetDevice = paintDevice->createCompositionSourceDevice(); targetDevice->setParentNode(node); indirect->setTemporaryTarget(targetDevice); indirect->setTemporaryCompositeOp(m_resources->compositeOp()); indirect->setTemporaryOpacity(m_resources->opacity()); indirect->setTemporarySelection(selection); QBitArray channelLockFlags = m_resources->channelLockFlags(); indirect->setTemporaryChannelFlags(channelLockFlags); } else { hasIndirectPainting = false; } } if(m_useMergeID){ m_transaction = new KisTransaction(name(), targetDevice,0,timedID(this->id())); } else{ m_transaction = new KisTransaction(name(), targetDevice); } initPainters(targetDevice, selection, hasIndirectPainting, indirectPaintingCompositeOp()); m_targetDevice = targetDevice; m_activeSelection = selection; // sanity check: selection should be applied only once if (selection && !m_painterInfos.isEmpty()) { KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); KIS_ASSERT_RECOVER_RETURN(hasIndirectPainting || m_painterInfos.first()->painter->selection()); KIS_ASSERT_RECOVER_RETURN(!hasIndirectPainting || !indirect->temporarySelection() || !m_painterInfos.first()->painter->selection()); } } void KisPainterBasedStrokeStrategy::finishStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisLayerSP layer = dynamic_cast(node.data()); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if(layer && indirect && indirect->hasTemporaryTarget()) { KUndo2MagicString transactionText = m_transaction->text(); m_transaction->end(); if(m_useMergeID){ indirect->mergeToLayer(layer, m_resources->postExecutionUndoAdapter(), transactionText,timedID(this->id())); } else{ indirect->mergeToLayer(layer, m_resources->postExecutionUndoAdapter(), transactionText); } } else { m_transaction->commit(m_resources->postExecutionUndoAdapter()); } delete m_transaction; deletePainters(); } void KisPainterBasedStrokeStrategy::cancelStrokeCallback() { KisNodeSP node = m_resources->currentNode(); KisIndirectPaintingSupport *indirect = dynamic_cast(node.data()); if(indirect && indirect->hasTemporaryTarget()) { delete m_transaction; deletePainters(); QRegion region = indirect->temporaryTarget()->region(); indirect->setTemporaryTarget(0); node->setDirty(region); } else { m_transaction->revert(); delete m_transaction; deletePainters(); } } diff --git a/krita/ui/widgets/kis_color_space_selector.cc b/krita/ui/widgets/kis_color_space_selector.cc index 610e290228..21102325db 100644 --- a/krita/ui/widgets/kis_color_space_selector.cc +++ b/krita/ui/widgets/kis_color_space_selector.cc @@ -1,299 +1,300 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Boudewijn Rempt * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_color_space_selector.h" +#include "kis_advanced_color_space_selector.h" #include #include #include #include #include #include #include #include #include #ifdef GHNS #include #include #endif #include #include #include #include "ui_wdgcolorspaceselector.h" struct KisColorSpaceSelector::Private { Ui_WdgColorSpaceSelector* colorSpaceSelector; QString knsrcFile; bool profileValid; QString defaultsuffix; }; KisColorSpaceSelector::KisColorSpaceSelector(QWidget* parent) : QWidget(parent), m_advancedSelector(0), d(new Private) { setObjectName("KisColorSpaceSelector"); d->colorSpaceSelector = new Ui_WdgColorSpaceSelector; d->colorSpaceSelector->setupUi(this); d->colorSpaceSelector->cmbColorModels->setIDList(KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::OnlyUserVisible)); fillCmbDepths(d->colorSpaceSelector->cmbColorModels->currentItem()); d->colorSpaceSelector->bnInstallProfile->setIcon(KisIconUtils::loadIcon("document-open")); d->colorSpaceSelector->bnInstallProfile->setToolTip( i18n("Open Color Profile") ); d->colorSpaceSelector->bnDownloadProfile->setIcon(KisIconUtils::loadIcon("download")); d->colorSpaceSelector->bnDownloadProfile->setToolTip( i18n("Download Color Profile") ); d->colorSpaceSelector->bnDownloadProfile->setEnabled( true ); d->colorSpaceSelector->bnDownloadProfile->hide(); d->colorSpaceSelector->bnUploadProfile->setIcon(KisIconUtils::loadIcon("arrow-up")); d->colorSpaceSelector->bnUploadProfile->setToolTip( i18n("Share Color Profile") ); d->colorSpaceSelector->bnUploadProfile->setEnabled( false ); d->colorSpaceSelector->bnUploadProfile->hide(); #ifdef GHNS d->colorSpaceSelector->bnUploadProfile->show(); d->colorSpaceSelector->bnDownloadProfile->show(); #endif connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbDepths(const KoID &))); connect(d->colorSpaceSelector->cmbColorDepth, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbColorModels, SIGNAL(activated(const KoID &)), this, SLOT(fillCmbProfiles())); connect(d->colorSpaceSelector->cmbProfile, SIGNAL(activated(const QString &)), this, SLOT(colorSpaceChanged())); connect(d->colorSpaceSelector->cmbProfile, SIGNAL(currentIndexChanged(int)), this, SLOT(buttonUpdate())); connect(d->colorSpaceSelector->bnInstallProfile, SIGNAL(clicked()), this, SLOT(installProfile())); connect(d->colorSpaceSelector->bnDownloadProfile, SIGNAL(clicked()), this, SLOT(downloadProfile())); connect(d->colorSpaceSelector->bnUploadProfile, SIGNAL(clicked()), this, SLOT(uploadProfile())); d->knsrcFile = "kritaiccprofiles.knsrc"; d->defaultsuffix = " "+i18nc("This is appended to the color profile which is the default for the given colorspace and bit-depth","(Default)"); connect(d->colorSpaceSelector->bnAdvanced, SIGNAL(clicked()), this, SLOT(slotOpenAdvancedSelector())); fillCmbProfiles(); } KisColorSpaceSelector::~KisColorSpaceSelector() { delete d->colorSpaceSelector; delete d; } void KisColorSpaceSelector::fillCmbProfiles() { QString s = KoColorSpaceRegistry::instance()->colorSpaceId(d->colorSpaceSelector->cmbColorModels->currentItem(), d->colorSpaceSelector->cmbColorDepth->currentItem()); d->colorSpaceSelector->cmbProfile->clear(); const KoColorSpaceFactory * csf = KoColorSpaceRegistry::instance()->colorSpaceFactory(s); if (csf == 0) return; QList profileList = KoColorSpaceRegistry::instance()->profilesFor(csf); QStringList profileNames; foreach(const KoColorProfile *profile, profileList) { profileNames.append(profile->name()); } qSort(profileNames); foreach(QString stringName, profileNames) { if (stringName==csf->defaultProfile()) { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName+d->defaultsuffix); } else { d->colorSpaceSelector->cmbProfile->addSqueezedItem(stringName); } } d->colorSpaceSelector->cmbProfile->setCurrent(csf->defaultProfile()+d->defaultsuffix); colorSpaceChanged(); } void KisColorSpaceSelector::fillCmbDepths(const KoID& id) { KoID activeDepth = d->colorSpaceSelector->cmbColorDepth->currentItem(); d->colorSpaceSelector->cmbColorDepth->clear(); QList depths = KoColorSpaceRegistry::instance()->colorDepthList(id, KoColorSpaceRegistry::OnlyUserVisible); d->colorSpaceSelector->cmbColorDepth->setIDList(depths); if (depths.contains(activeDepth)) { d->colorSpaceSelector->cmbColorDepth->setCurrent(activeDepth); } } const KoColorSpace* KisColorSpaceSelector::currentColorSpace() { QString profilenamestring = d->colorSpaceSelector->cmbProfile->itemHighlighted(); if (profilenamestring.contains(d->defaultsuffix)) { profilenamestring.remove(d->defaultsuffix); return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } else { return KoColorSpaceRegistry::instance()->colorSpace( d->colorSpaceSelector->cmbColorModels->currentItem().id(), d->colorSpaceSelector->cmbColorDepth->currentItem().id(), profilenamestring); } } void KisColorSpaceSelector::setCurrentColorModel(const KoID& id) { d->colorSpaceSelector->cmbColorModels->setCurrent(id); fillCmbDepths(id); } void KisColorSpaceSelector::setCurrentColorDepth(const KoID& id) { d->colorSpaceSelector->cmbColorDepth->setCurrent(id); fillCmbProfiles(); } void KisColorSpaceSelector::setCurrentProfile(const QString& name) { d->colorSpaceSelector->cmbProfile->setCurrent(name); } void KisColorSpaceSelector::setCurrentColorSpace(const KoColorSpace* colorSpace) { setCurrentColorModel(colorSpace->colorModelId()); setCurrentColorDepth(colorSpace->colorDepthId()); setCurrentProfile(colorSpace->profile()->name()); } void KisColorSpaceSelector::colorSpaceChanged() { bool valid = d->colorSpaceSelector->cmbProfile->count() != 0; d->profileValid = valid; emit(selectionChanged(valid)); if(valid) { emit colorSpaceChanged(currentColorSpace()); QString text = currentColorSpace()->profile()->name(); } } void KisColorSpaceSelector::installProfile() { QStringList mime; mime << "*.icm" << "*.icc"; KoFileDialog dialog(this, KoFileDialog::OpenFiles, "OpenDocumentICC"); dialog.setCaption(i18n("Install Color Profiles")); dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)); dialog.setNameFilters(mime); QStringList profileNames = dialog.filenames(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); QString saveLocation = KoResourcePaths::saveLocation("icc_profiles"); foreach (const QString &profileName, profileNames) { QUrl file(profileName); if (!QFile::copy(profileName, saveLocation + file.fileName())) { dbgKrita << "Could not install profile!"; return; } iccEngine->addProfile(saveLocation + file.fileName()); } fillCmbProfiles(); } void KisColorSpaceSelector::downloadProfile() { #ifdef GHNS KNS3::DownloadDialog dialog( "kritaiccprofiles.knsrc", this); dialog.exec(); KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); Q_ASSERT(iccEngine); foreach (const KNS3::Entry& e, dialog.changedEntries()) { foreach(const QString &file, e.installedFiles()) { QFileInfo fi(file); iccEngine->addProfile( fi.absolutePath()+'/'+fi.fileName()); } foreach(const QString &file, e.uninstalledFiles()) { QFileInfo fi(file); iccEngine->removeProfile( fi.absolutePath()+'/'+fi.fileName()); } } fillCmbProfiles(); #endif } void KisColorSpaceSelector::uploadProfile() { #ifdef GHNS KNS3::UploadDialog dialog("kritaiccprofiles.knsrc", this); const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(d->colorSpaceSelector->cmbProfile->currentText()); if(!profile) return; dialog.setUploadFile(QUrl::fromLocalFile(profile->fileName())); dialog.setUploadName(profile->name()); dialog.exec(); #endif } void KisColorSpaceSelector::buttonUpdate() { const KoColorProfile * profile = KoColorSpaceRegistry::instance()->profileByName(d->colorSpaceSelector->cmbProfile->currentText()); if(!profile) return; QFileInfo fileInfo(profile->fileName()); if(fileInfo.isWritable()) { d->colorSpaceSelector->bnUploadProfile->setEnabled( true ); return; } d->colorSpaceSelector->bnUploadProfile->setEnabled( false ); } void KisColorSpaceSelector::slotOpenAdvancedSelector() { if (!m_advancedSelector) { m_advancedSelector = new KisAdvancedColorSpaceSelector(this, "Select a Colorspace"); m_advancedSelector->setModal(true); m_advancedSelector->setCurrentColorSpace(currentColorSpace()); connect(m_advancedSelector, SIGNAL(selectionChanged(bool)), this, SLOT(slotProfileValid(bool)) ); } QDialog::DialogCode result = (QDialog::DialogCode)m_advancedSelector->exec(); if (result) { if (d->profileValid==true) { setCurrentColorSpace(m_advancedSelector->currentColorSpace()); } } } void KisColorSpaceSelector::slotProfileValid(bool valid) { d->profileValid = valid; } diff --git a/krita/ui/widgets/kis_color_space_selector.h b/krita/ui/widgets/kis_color_space_selector.h index 1dfd4360ac..6ea51fb94e 100644 --- a/krita/ui/widgets/kis_color_space_selector.h +++ b/krita/ui/widgets/kis_color_space_selector.h @@ -1,67 +1,66 @@ /* * Copyright (C) 2007 Cyrille Berger * Copyright (C) 2011 Srikanth Tiyyagura * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KIS_COLOR_SPACE_SELECTOR_H_ #define _KIS_COLOR_SPACE_SELECTOR_H_ #include -#include "kis_advanced_color_space_selector.h" #include class KoID; class KoColorSpace; class KisAdvancedColorSpaceSelector; class KRITAUI_EXPORT KisColorSpaceSelector : public QWidget { Q_OBJECT public: KisColorSpaceSelector(QWidget* parent); ~KisColorSpaceSelector(); const KoColorSpace* currentColorSpace(); void setCurrentColorModel(const KoID& id); void setCurrentColorDepth(const KoID& id); void setCurrentProfile(const QString& name); void setCurrentColorSpace(const KoColorSpace* colorSpace); Q_SIGNALS: /** * This signal is emitted when a new color space is selected. * @param valid indicates if the color space can be used */ void selectionChanged(bool valid); /// This signal is emitted, when a new color space is selected, that can be used (eg is valid) void colorSpaceChanged(const KoColorSpace*); private Q_SLOTS: void fillCmbDepths(const KoID& idd); void fillCmbProfiles(); void colorSpaceChanged(); void installProfile(); void uploadProfile(); void downloadProfile(); void buttonUpdate(); void slotOpenAdvancedSelector(); void slotProfileValid(bool valid); private: struct Private; KisAdvancedColorSpaceSelector *m_advancedSelector; Private * const d; }; #endif diff --git a/krita/ui/widgets/kis_gradient_chooser.h b/krita/ui/widgets/kis_gradient_chooser.h index 287c6580f4..d8888d331c 100644 --- a/krita/ui/widgets/kis_gradient_chooser.h +++ b/krita/ui/widgets/kis_gradient_chooser.h @@ -1,81 +1,80 @@ /* * Copyright (c) 2004 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GRADIENT_CHOOSER_H_ #define KIS_GRADIENT_CHOOSER_H_ #include #include class KoSegmentGradient; class KisViewManager; class QLabel; class QPushButton; -class KisViewManager; class KisAutogradient; class KoResource; class KoResourceItemChooser; class KisCustomGradientDialog : public KoDialog { Q_OBJECT public: KisCustomGradientDialog(KoSegmentGradient* gradient, QWidget * parent, const char *name); private: KisAutogradient * m_page; }; class KisGradientChooser : public QFrame { Q_OBJECT public: KisGradientChooser(QWidget *parent = 0, const char *name = 0); virtual ~KisGradientChooser(); /// Gets the currently selected resource /// @returns the selected resource, 0 is no resource is selected KoResource *currentResource(); void setCurrentResource(KoResource *resource); void setCurrentItem(int row, int column); Q_SIGNALS: /// Emitted when a resource was selected void resourceSelected(KoResource * resource); private Q_SLOTS: virtual void update(KoResource * resource); void addGradient(); void editGradient(); private: QLabel *m_lbName; KoResourceItemChooser * m_itemChooser; QPushButton* m_editGradient; }; #endif // KIS_GRADIENT_CHOOSER_H_ diff --git a/krita/ui/widgets/kis_multipliers_double_slider_spinbox.h b/krita/ui/widgets/kis_multipliers_double_slider_spinbox.h index 24dd8e42ae..5450bcb9fa 100644 --- a/krita/ui/widgets/kis_multipliers_double_slider_spinbox.h +++ b/krita/ui/widgets/kis_multipliers_double_slider_spinbox.h @@ -1,61 +1,60 @@ /* This file is part of the KDE project * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KIS_MULTIPLIERS_DOUBLE_SLIDER_SPINBOX_H_ #define _KIS_MULTIPLIERS_DOUBLE_SLIDER_SPINBOX_H_ #include #include -class KisDoubleSliderSpinBox; /** * This class add a combobox to a \ref KisDoubleSliderSpinBox which * allows to define a multiplier to let the user change the range. */ class KRITAUI_EXPORT KisMultipliersDoubleSliderSpinBox : public QWidget { Q_OBJECT public: KisMultipliersDoubleSliderSpinBox(QWidget* _parent = 0); ~KisMultipliersDoubleSliderSpinBox(); void addMultiplier(double v); /** * Set the range for the 1.0 multiplier */ void setRange(qreal minimum, qreal maximum, int decimals = 0); ///Get the value, don't use value() qreal value(); ///Set the value, don't use setValue() void setValue(qreal value); void setExponentRatio(qreal dbl); Q_SIGNALS: void valueChanged(qreal value); private: Q_PRIVATE_SLOT(d, void updateRange()) struct Private; Private* const d; }; #endif diff --git a/krita/ui/widgets/kis_multipliers_double_slider_spinbox_p.h b/krita/ui/widgets/kis_multipliers_double_slider_spinbox_p.h index 9d5af97c2a..35bc361402 100644 --- a/krita/ui/widgets/kis_multipliers_double_slider_spinbox_p.h +++ b/krita/ui/widgets/kis_multipliers_double_slider_spinbox_p.h @@ -1,38 +1,37 @@ /* This file is part of the KDE project * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef kis_multipliers_double_slider_spinbox_p_h #define kis_multipliers_double_slider_spinbox_p_h -#include "kis_multipliers_double_slider_spinbox.h" #include "ui_wdgmultipliersdoublesliderspinbox.h" #include "kis_debug.h" struct KisMultipliersDoubleSliderSpinBox::Private { qreal currentMultiplier(); /// Update the range of the slider depending on the currentMultiplier void updateRange(); Ui::WdgMultipliersDoubleSliderSpinBox form; qreal min, max; int decimals; }; #endif diff --git a/krita/ui/widgets/kis_paintop_presets_popup.h b/krita/ui/widgets/kis_paintop_presets_popup.h index 57853c0c2b..2d425ffb3e 100644 --- a/krita/ui/widgets/kis_paintop_presets_popup.h +++ b/krita/ui/widgets/kis_paintop_presets_popup.h @@ -1,110 +1,109 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2008 * Copyright (C) 2010 Lukáš Tvrdý * Copyright (C) 2011 Silvio Heinrich * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KIS_PAINTOP_PRESETS_POPUP_H #define KIS_PAINTOP_PRESETS_POPUP_H #include #include #include #include #include class QString; -class KisPaintOpPreset; class KisCanvasResourceProvider; class KoResource; /** * Popup widget for presets with built-in functionality * for adding and removing presets. */ class KisPaintOpPresetsPopup : public QWidget { Q_OBJECT public: KisPaintOpPresetsPopup(KisCanvasResourceProvider * resourceProvider, QWidget * parent = 0); ~KisPaintOpPresetsPopup(); void setPaintOpSettingsWidget(QWidget * widget); /** * @return the name entered in the preset name lineedit */ QString getPresetName() const; ///Image for preset preview ///@return image cut out from the scratchpad QImage cutOutOverlay(); void setPaintOpList(const QList& list); void setCurrentPaintOp(const QString & paintOpId); QString currentPaintOp(); ///fill the cutoutOverlay rect with the cotent of an image, used to get the image back when selecting a preset ///@param image image that will be used, should be image of an existing preset resource void setPresetImage(const QImage& image); virtual void resizeEvent(QResizeEvent* ); bool detached() const; void updateViewSettings(); protected: void contextMenuEvent(QContextMenuEvent *); void hideEvent(QHideEvent *); void showEvent(QShowEvent *); public Q_SLOTS: void slotWatchPresetNameLineEdit(); void switchDetached(bool show = true); void hideScratchPad(); void showScratchPad(); void resourceSelected(KoResource* resource); void updateThemedIcons(); Q_SIGNALS: void savePresetClicked(); void defaultPresetClicked(); void paintopActivated(const QString& presetName); void signalResourceSelected(KoResource* resource); void reloadPresetClicked(); void dirtyPresetToggled(bool value); void eraserBrushSizeToggled(bool value); void sizeChanged(); private Q_SLOTS: void slotSwitchPresetStrip(bool visible); void slotSwitchScratchpad(bool visible); private: struct Private; Private * const m_d; }; #endif diff --git a/krita/ui/widgets/squeezedcombobox.h b/krita/ui/widgets/squeezedcombobox.h index bc71cc358f..205c0bf0a9 100644 --- a/krita/ui/widgets/squeezedcombobox.h +++ b/krita/ui/widgets/squeezedcombobox.h @@ -1,136 +1,135 @@ /* ============================================================ * Author: Tom Albers * Date : 2005-01-01 * Description : * * Copyright 2005 by Tom Albers * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ /** @file widgets/squeezedcombobox.h */ #ifndef SQUEEZEDCOMBOBOX_H #define SQUEEZEDCOMBOBOX_H class QTimer; class QResizeEvent; class QWidget; // Qt includes. #include #include #include -class SqueezedComboBox; /** @class SqueezedComboBox * * This widget is a QComboBox, but then a little bit * different. It only shows the right part of the items * depending on de size of the widget. When it is not * possible to show the complete item, it will be shortened * and "..." will be prepended. * * @image html squeezedcombobox.png "This is how it looks" * @author Tom Albers */ class KRITAUI_EXPORT SqueezedComboBox : public QComboBox { Q_OBJECT public: /** * Constructor * @param parent parent widget * @param name name to give to the widget */ SqueezedComboBox(QWidget *parent = 0, const char *name = 0); /** * destructor */ virtual ~SqueezedComboBox(); /** * * Returns true if the combobox contains the original (not-squeezed) * version of text. * @param text the original (not-squeezed) text to check for */ bool contains(const QString & text) const; /** * * Returns index of a orinal text, -1 if the text isn't found * @param text the original (not-squeezed) text to search for */ qint32 findOriginalText(const QString & text) const; /** * This inserts a item to the list. See QComboBox::insertItem() * for detaills. Please do not use QComboBox::insertItem() to this * widget, as that will fail. * @param newItem the original (long version) of the item which needs * to be added to the combobox * @param index the position in the widget. */ void insertSqueezedItem(const QString& newItem, int index, QVariant userData = QVariant()); /** * Append an item. * @param newItem the original (long version) of the item which needs * to be added to the combobox */ void addSqueezedItem(const QString& newItem, QVariant userData = QVariant()); /** * Set the current item to the one matching the given text. * * @param itemText the original (long version) of the item text */ void setCurrent(const QString& itemText); /** * This method returns the full text (not squeezed) of the currently * highlighted item. * @return full text of the highlighted item */ QString itemHighlighted(); /** * Sets the sizeHint() of this widget. */ virtual QSize sizeHint() const; private Q_SLOTS: void slotTimeOut(); private: void resizeEvent(QResizeEvent *); QString squeezeText(const QString& original); // Prevent these from being used. void setCurrentText(const QString& itemText); void insertItem(const QString &text); void insertItem(qint32 index, const QString &text); void addItem(const QString &text); QMap m_originalItems; QTimer* m_timer; }; #endif // SQUEEZEDCOMBOBOX_H