...
 
Commits (48)
......@@ -103,14 +103,15 @@ bool KisPngBrush::loadFromDevice(QIODevice *dev)
return false;
}
setBrushTipImage(image);
setValid(true);
if (brushTipImage().isGrayscale()) {
if (image.allGray()) {
setBrushTipImage(image.convertToFormat(QImage::Format_Grayscale8));
setBrushType(MASK);
setHasColor(false);
}
else {
setBrushTipImage(image);
setBrushType(IMAGE);
setHasColor(true);
}
......
......@@ -637,6 +637,42 @@ void TestSvgText::testTextSpacing()
}
void TestSvgText::testTextTabSpacing()
{
const QString data =
"<svg width=\"100px\" height=\"30px\""
" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
"<g id=\"test\">"
" <rect id=\"boundingRect\" x=\"5\" y=\"5\" width=\"89\" height=\"120\""
" fill=\"none\" stroke=\"red\"/>"
" <text id=\"testRect\" x=\"5\" y=\"24\" "
" font-family=\"DejaVu Sans\" font-size=\"15\" fill=\"blue\" >"
" <tspan x=\"10\" dy=\"1.0em\"> Lorem</tspan>"
" <tspan x=\"10\" dy=\"2.0em\"> ipsum</tspan>"
" <tspan x=\"10\" dy=\"2.0em\">dolor sit amet,</tspan>"
" <tspan x=\"10\" dy=\"2.0em\"> consectetur adipiscing elit.</tspan>"
" </text>"
"</g>"
"</svg>";
SvgRenderTester t (data);
t.setFuzzyThreshold(5);
t.test_standard("text_tab_spacing", QSize(400, 170), 72.0);
KoSvgTextChunkShape *baseShape = toChunkShape(t.findShape("testRect"));
QVERIFY(baseShape);
// root shape is not just a chunk!
QVERIFY(dynamic_cast<KoSvgTextShape*>(baseShape));
}
void TestSvgText::testTextDecorations()
{
const QString data =
......
......@@ -38,6 +38,7 @@ private Q_SLOTS:
void testHindiText();
void testTextBaselineShift();
void testTextSpacing();
void testTextTabSpacing();
void testTextDecorations();
void testRightToLeft();
......
......@@ -344,9 +344,17 @@ struct LayoutChunkWrapper
}
if (startPos <= lastPos) {
// defines the number of columns to look for glyphs
const int numChars = lastPos - startPos + 1;
// Tabs break the normal column flow
// grow to avoid missing glyphs
int charOffset = 0;
while (line.textLength() < numChars) {
line.setNumColumns(numChars + charOffset);
charOffset++;
}
line.setNumColumns(numChars);
line.setPosition(currentTextPos - QPointF(0, line.ascent()));
currentTextPos.rx() += line.horizontalAdvance();
......@@ -609,12 +617,13 @@ KoShape *KoSvgTextShapeFactory::createShape(const KoProperties *params, KoDocume
shapeRect = rect.toRectF();
}
KoShapeController *controller = documentResources->shapeController();
KoSvgTextShapeMarkupConverter converter(shape);
converter.convertFromSvg(svgText,
defs,
shapeRect,
documentResources->shapeController()->pixelsPerInch());
controller ? controller->pixelsPerInch() : 72);
shape->setPosition(shapeRect.topLeft());
......
......@@ -98,6 +98,7 @@ set(kritaimage_LIB_SRCS
commands/kis_reselect_global_selection_command.cpp
commands/KisReselectActiveSelectionCommand.cpp
commands/kis_set_global_selection_command.cpp
commands/KisNodeRenameCommand.cpp
commands_new/kis_saved_commands.cpp
commands_new/kis_processing_command.cpp
commands_new/kis_image_resize_command.cpp
......
/*
* dlg_histogram.h -- part of KimageShop^WKrayon^WKrita
*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* Copyright (c) 2019 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
......@@ -17,39 +15,27 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DLG_HISTOGRAM
#define DLG_HISTOGRAM
#include <KoDialog.h>
#include "KisNodeRenameCommand.h"
#include "kis_types.h"
#include <klocalizedstring.h>
#include "kis_node.h"
#include "commands/kis_node_commands.h"
class KisHistogramWidget;
/**
* This dialog shows the histogram for the (selected) portion
* of the current layer.
*
* XXX: Also for complete image?
*/
class DlgHistogram: public KoDialog
KisNodeRenameCommand::KisNodeRenameCommand(KisNodeSP node, const QString &oldName, const QString &newName)
: KisNodeCommand(kundo2_i18n("Node Rename"), node)
{
Q_OBJECT
public:
DlgHistogram(QWidget * parent = 0,
const char* name = 0);
~DlgHistogram() override;
m_oldName = oldName;
m_newName = newName;
}
void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds);
private Q_SLOTS:
void okClicked();
private:
KisHistogramWidget * m_page;
};
void KisNodeRenameCommand::redo()
{
m_node->setName(m_newName);
}
#endif // DLG_HISTOGRAM
void KisNodeRenameCommand::undo()
{
m_node->setName(m_oldName);
}
/*
* histogram.h -- Part of Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2019 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
......@@ -17,23 +15,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISNODERENAMECOMMAND_H
#define KISNODERENAMECOMMAND_H
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include <QVariant>
#include <KisActionPlugin.h>
#include "kis_node_command.h"
class Histogram : public KisActionPlugin
/// The command for setting the node's name
class KRITAIMAGE_EXPORT KisNodeRenameCommand : public KisNodeCommand
{
Q_OBJECT
public:
Histogram(QObject *parent, const QVariantList &);
~Histogram() override;
KisNodeRenameCommand(KisNodeSP node,
const QString &oldName,
const QString &newName);
void redo() override;
void undo() override;
private:
private Q_SLOTS:
void slotActivated();
QString m_oldName;
QString m_newName;
};
#endif // HISTOGRAM_H
#endif // KISNODERENAMECOMMAND_H
......@@ -21,8 +21,6 @@
#include <KoCompositeOp.h>
#include "kis_node.h"
#include "commands/kis_node_commands.h"
#include "kis_paint_device.h"
KisNodeCompositeOpCommand::KisNodeCompositeOpCommand(KisNodeSP node, const QString& oldCompositeOp,
const QString& newCompositeOp) :
......
......@@ -68,8 +68,8 @@ bool KisConvolutionPainter::useFFTImplemenation(const KisConvolutionKernelSP ker
result =
m_enginePreference == FFTW ||
(m_enginePreference == NONE &&
kernel->width() > THRESHOLD_SIZE &&
kernel->height() > THRESHOLD_SIZE);
(kernel->width() > THRESHOLD_SIZE ||
kernel->height() > THRESHOLD_SIZE));
#else
Q_UNUSED(kernel);
#endif
......
......@@ -2075,6 +2075,17 @@ KisPaintDeviceFramesInterface* KisPaintDevice::framesInterface()
return m_d->framesInterface.data();
}
void KisPaintDevice::debugPaintDevice(const QString &basename) const
{
static int i = 0;
QString filename = QString ("%1_%2.png").arg(QString::number(i), 6, QChar('0')).arg(basename);
QImage image = convertToQImage(0);
image.save(filename);
i++;
}
/******************************************************************/
/* KisPaintDeviceFramesInterface */
/******************************************************************/
......
......@@ -742,6 +742,12 @@ public:
*/
KisPaintDeviceFramesInterface* framesInterface();
/**
* @brief debugPaintDevice save the current paint device to a numbered PNG image
* @param basename the basename for the file.
*/
void debugPaintDevice(const QString &basename) const;
public:
/**
......
......@@ -323,10 +323,25 @@ struct KisSuspendProjectionUpdatesStrokeStrategy::Private
image->signalRouter()->emitNotifyBatchUpdateEnded();
m_strategy->m_d->sanityResumingFinished = true;
m_strategy->m_d->accumulatedDirtyRects.clear();
KIS_SAFE_ASSERT_RECOVER_NOOP(m_strategy->m_d->usedFilters.isEmpty());
}
void undo() override {
KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "why the heck we are undoing the last job of the stroke?!");
/**
* Even though this comand is the last command of the stroke is can
* still be undone by suspendStrokeCallback(). It happens when a LodN
* stroke is started right after the last job of resume strategy was
* being executed. In such a case new stroke is placed right in front
* of our resume strategy and all the resuming work is undone (mimicing
* a normal suspend strategy).
*
* The only thing we should control here is whether the state of the
* stroke is reset to default. Otherwise we'll do all the updates twice.
*/
KIS_SAFE_ASSERT_RECOVER_NOOP(m_strategy->m_d->usedFilters.isEmpty());
KIS_SAFE_ASSERT_RECOVER_NOOP(m_strategy->m_d->accumulatedDirtyRects.isEmpty());
m_strategy->m_d->sanityResumingFinished = false;
......@@ -472,6 +487,7 @@ void KisSuspendProjectionUpdatesStrokeStrategy::Private::tryIssueRecordedDirtyRe
Q_FOREACH (QSharedPointer<Private::SuspendLod0Updates> filter, usedFilters) {
filter->notifyUpdates(image.data());
}
usedFilters.clear();
}
void KisSuspendProjectionUpdatesStrokeStrategy::cancelStrokeCallback()
......@@ -506,7 +522,17 @@ void KisSuspendProjectionUpdatesStrokeStrategy::cancelStrokeCallback()
void KisSuspendProjectionUpdatesStrokeStrategy::suspendStrokeCallback()
{
KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->suspend || !m_d->sanityResumingFinished);
/**
* The resume stroke can be suspended even when all its jobs are completed.
* In such a case, we should just ensure that all the internal state is reset
* to default.
*/
KIS_SAFE_ASSERT_RECOVER_NOOP(m_d->suspend ||
!m_d->sanityResumingFinished ||
(m_d->sanityResumingFinished &&
m_d->usedFilters.isEmpty() &&
m_d->accumulatedDirtyRects.isEmpty()));
for (auto it = m_d->executedCommands.rbegin(); it != m_d->executedCommands.rend(); ++it) {
(*it)->undo();
......
......@@ -304,13 +304,13 @@ qreal KoColor::opacityF() const
return m_colorSpace->opacityF(m_data);
}
KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId)
KoColor KoColor::fromXML(const QDomElement& elt, const QString& channelDepthId)
{
bool ok;
return fromXML(elt, bitDepthId, &ok);
return fromXML(elt, channelDepthId, &ok);
}
KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId, bool* ok)
KoColor KoColor::fromXML(const QDomElement& elt, const QString& channelDepthId, bool* ok)
{
*ok = true;
QString modelId;
......@@ -336,7 +336,7 @@ KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId, bool
profileName.clear();
}
}
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, bitDepthId, profileName);
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, channelDepthId, profileName);
if (cs == 0) {
QList<KoID> list = KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces);
if (!list.empty()) {
......@@ -354,6 +354,29 @@ KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId, bool
}
}
QString KoColor::toXML() const
{
QDomDocument cdataDoc = QDomDocument("color");
QDomElement cdataRoot = cdataDoc.createElement("color");
cdataDoc.appendChild(cdataRoot);
cdataRoot.setAttribute("channeldepth", colorSpace()->colorDepthId().id());
toXML(cdataDoc, cdataRoot);
return cdataDoc.toString();
}
KoColor KoColor::fromXML(const QString &xml)
{
KoColor c;
QDomDocument doc;
if (doc.setContent(xml)) {
QDomElement e = doc.documentElement().firstChild().toElement();
QString channelDepthID = e.attribute("channeldepth", Integer16BitsColorDepthID.id());
bool ok;
c = KoColor::fromXML(e, channelDepthID, &ok);
}
return c;
}
QString KoColor::toQString(const KoColor &color)
{
QStringList ls;
......
......@@ -218,21 +218,21 @@ public:
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
* @param bitDepthId the bit depth is unspecified by the spec, this allow to select
* @param channelDepthId the bit depth is unspecified by the spec, this allow to select
* a preferred bit depth for creating the KoColor object (if that
* bit depth isn't available, this function will randomly select
* an other bit depth)
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId);
static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId);
/**
* Unserialize a color following Create's swatch color specification available
* at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format
*
* @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
* @param bitDepthId the bit depth is unspecified by the spec, this allow to select
* @param channelDepthId the bit depth is unspecified by the spec, this allow to select
* a preferred bit depth for creating the KoColor object (if that
* bit depth isn't available, this function will randomly select
* an other bit depth)
......@@ -240,7 +240,24 @@ public:
* @return the unserialize color, or an empty color object if the function failed
* to unserialize the color
*/
static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId, bool* ok);
static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId, bool* ok);
/**
* @brief toXML creates a string with XML that represents the current color. The XML
* is extended with a "channeldepth" attribute so we can restore the color to the same
* channel depth.
* @return a valid XML document in a string
*/
QString toXML() const;
/**
* @brief fromXML restores a KoColor from a string saved with toXML(). If the
* string does not contain the "channeldepth" attribute, 16 bit integer is assumed.
* @param xml a valid XML document
* @return a new KoColor object
*/
static KoColor fromXML(const QString &xml);
/**
* @brief toQString create a user-visible string of the channel names and the channel values
......
......@@ -33,6 +33,7 @@
#include <QDomElement>
#include <QBuffer>
#include <kis_dom_utils.h>
#include "KoColorSpaceRegistry.h"
#include "KoColorSpace.h"
......@@ -281,19 +282,19 @@ void KoSegmentGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const
QDomElement segmentElt = doc.createElement("segment");
QDomElement start = doc.createElement("start");
QDomElement end = doc.createElement("end");
segmentElt.setAttribute("start-offset", segment->startOffset());
segmentElt.setAttribute("start-offset", KisDomUtils::toString(segment->startOffset()));
const KoColor startColor = segment->startColor();
segmentElt.setAttribute("start-bitdepth", startColor.colorSpace()->colorDepthId().id());
segmentElt.setAttribute("start-alpha", startColor.opacityF());
segmentElt.setAttribute("start-alpha", KisDomUtils::toString(startColor.opacityF()));
startColor.toXML(doc, start);
segmentElt.setAttribute("middle-offset", segment->middleOffset());
segmentElt.setAttribute("end-offset", segment->endOffset());
segmentElt.setAttribute("middle-offset", KisDomUtils::toString(segment->middleOffset()));
segmentElt.setAttribute("end-offset", KisDomUtils::toString(segment->endOffset()));
const KoColor endColor = segment->endColor();
segmentElt.setAttribute("end-bitdepth", endColor.colorSpace()->colorDepthId().id());
segmentElt.setAttribute("end-alpha", endColor.opacityF());
segmentElt.setAttribute("end-alpha", KisDomUtils::toString(endColor.opacityF()));
endColor.toXML(doc, end);
segmentElt.setAttribute("interpolation", segment->interpolation());
segmentElt.setAttribute("color-interpolation", segment->colorInterpolation());
segmentElt.setAttribute("interpolation", KisDomUtils::toString(segment->interpolation()));
segmentElt.setAttribute("color-interpolation", KisDomUtils::toString(segment->colorInterpolation()));
segmentElt.appendChild(start);
segmentElt.appendChild(end);
gradientElt.appendChild(segmentElt);
......@@ -305,19 +306,19 @@ KoSegmentGradient KoSegmentGradient::fromXML(const QDomElement &elt)
KoSegmentGradient gradient;
QDomElement segmentElt = elt.firstChildElement("segment");
while (!segmentElt.isNull()) {
int interpolation = segmentElt.attribute("interpolation", "0.0").toInt();
int colorInterpolation = segmentElt.attribute("color-interpolation", "0.0").toInt();
double startOffset = segmentElt.attribute("start-offset", "0.0").toDouble();
qreal middleOffset = segmentElt.attribute("middle-offset", "0.0").toDouble();
qreal endOffset = segmentElt.attribute("end-offset", "0.0").toDouble();
int interpolation = KisDomUtils::toInt(segmentElt.attribute("interpolation", "0.0"));
int colorInterpolation = KisDomUtils::toInt(segmentElt.attribute("color-interpolation", "0.0"));
double startOffset = KisDomUtils::toDouble(segmentElt.attribute("start-offset", "0.0"));
qreal middleOffset = KisDomUtils::toDouble(segmentElt.attribute("middle-offset", "0.0"));
qreal endOffset = KisDomUtils::toDouble(segmentElt.attribute("end-offset", "0.0"));
QDomElement start = segmentElt.firstChildElement("start");
QString startBitdepth = segmentElt.attribute("start-bitdepth", Integer8BitsColorDepthID.id());
QColor left = KoColor::fromXML(start.firstChildElement(), startBitdepth).toQColor();
left.setAlphaF(segmentElt.attribute("start-alpha", "1.0").toDouble());
left.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("start-alpha", "1.0")));
QString endBitdepth = segmentElt.attribute("end-bitdepth", Integer8BitsColorDepthID.id());
QDomElement end = segmentElt.firstChildElement("end");
QColor right = KoColor::fromXML(end.firstChildElement(), endBitdepth).toQColor();
right.setAlphaF(segmentElt.attribute("end-alpha", "1.0").toDouble());
right.setAlphaF(KisDomUtils::toDouble(segmentElt.attribute("end-alpha", "1.0")));
gradient.createSegment(interpolation, colorInterpolation, startOffset, endOffset, middleOffset, left, right);
segmentElt = segmentElt.nextSiblingElement("segment");
}
......
......@@ -34,6 +34,8 @@
#include "KoColorSpaceRegistry.h"
#include "KoMixColorsOp.h"
#include "kis_dom_utils.h"
#include <math.h>
#include <KoColorModelStandardIds.h>
......@@ -535,12 +537,12 @@ QString KoStopGradient::defaultFileExtension() const
void KoStopGradient::toXML(QDomDocument &doc, QDomElement &gradientElt) const
{
gradientElt.setAttribute("type", "stop");
for (int s = 0; s<m_stops.size(); s++) {
for (int s = 0; s < m_stops.size(); s++) {
KoGradientStop stop = m_stops.at(s);
QDomElement stopElt = doc.createElement("stop");
stopElt.setAttribute("offset", stop.first);
stopElt.setAttribute("offset", KisDomUtils::toString(stop.first));
stopElt.setAttribute("bitdepth", stop.second.colorSpace()->colorDepthId().id());
stopElt.setAttribute("alpha", stop.second.opacityF());
stopElt.setAttribute("alpha", KisDomUtils::toString(stop.second.opacityF()));
stop.second.toXML(doc, stopElt);
gradientElt.appendChild(stopElt);
}
......@@ -552,10 +554,10 @@ KoStopGradient KoStopGradient::fromXML(const QDomElement &elt)
QList<KoGradientStop> stops;
QDomElement stopElt = elt.firstChildElement("stop");
while (!stopElt.isNull()) {
qreal offset = stopElt.attribute("offset", "0").toDouble();
qreal offset = KisDomUtils::toDouble(stopElt.attribute("offset", "0.0"));
QString bitDepth = stopElt.attribute("bitdepth", Integer8BitsColorDepthID.id());
KoColor color = KoColor::fromXML(stopElt.firstChildElement(), bitDepth);
color.setOpacity(stopElt.attribute("alpha", "1.0").toDouble());
color.setOpacity(KisDomUtils::toDouble(stopElt.attribute("alpha", "1.0")));
stops.append(KoGradientStop(offset, color));
stopElt = stopElt.nextSiblingElement("stop");
}
......
......@@ -87,4 +87,14 @@ void TestKoColor::testConversion()
kc.convertTo(csDst);
}
void TestKoColor::testSimpleSerialization()
{
QColor c = Qt::green;
KoColor k;
k.fromQColor(c);
QString xml = k.toXML();
KoColor k2 = KoColor::fromXML(xml);
QVERIFY(k2.colorSpace() == k.colorSpace());
}
KISTEST_MAIN(TestKoColor)
......@@ -31,6 +31,7 @@ private:
private Q_SLOTS:
void testSerialization();
void testConversion();
void testSimpleSerialization();
};
#endif
......
......@@ -78,7 +78,7 @@ namespace KoXml
* Note: do *NOT* use getElementsByTagNameNS, it's recursive!
*/
KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node,
const QString& nsURI, const QString& localName);
const QString& nsURI, const QString& localName);
/**
* A namespace-aware version of QDomNode::namedItem().
......@@ -92,8 +92,8 @@ KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node,
* the office-text-content-prelude condition as @a KoXmlNamedItemType .
*/
KRITASTORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode& node,
const QString& nsURI, const QString& localName,
KoXmlNamedItemType type);
const QString& nsURI, const QString& localName,
KoXmlNamedItemType type);
/**
* Explicitly load child nodes of specified node, up to given depth.
......@@ -155,8 +155,8 @@ KRITASTORE_EXPORT QDomDocument asQDomDocument(const KoXmlDocument& document);
* Note: it is assumed that the XML uses UTF-8 encoding.
*/
KRITASTORE_EXPORT bool setDocument(KoXmlDocument& doc, QIODevice* device,
bool namespaceProcessing, QString* errorMsg = 0,
int* errorLine = 0, int* errorColumn = 0);
bool namespaceProcessing, QString* errorMsg = 0,
int* errorLine = 0, int* errorColumn = 0);
}
/**
......@@ -177,7 +177,7 @@ KRITASTORE_EXPORT bool setDocument(KoXmlDocument& doc, QIODevice* device,
*/
#define forEachElement( elem, parent ) \
for ( KoXmlNode _node = parent.firstChild(); !_node.isNull(); _node = _node.nextSibling() ) \
if ( ( elem = _node.toElement() ).isNull() ) {} else
if ( ( elem = _node.toElement() ).isNull() ) {} else
#endif // KO_XMLREADER_H
......@@ -109,7 +109,6 @@ set(kritaui_LIB_SRCS
kis_splash_screen.cpp
kis_filter_manager.cc
kis_filters_model.cc
kis_histogram_view.cc
KisImageBarrierLockerWithFeedback.cpp
kis_image_manager.cc
kis_image_view_converter.cpp
......@@ -360,6 +359,7 @@ set(kritaui_LIB_SRCS
KisTemplateTree.cpp
KisUndoActionsUpdateManager.cpp
KisView.cpp
KisImportExportErrorCode.cpp
thememanager.cpp
......
......@@ -1581,7 +1581,11 @@ void KisDocument::slotUndoStackCleanChanged(bool value)
void KisDocument::slotConfigChanged()
{
KisConfig cfg(true);
d->undoStack->setUndoLimit(cfg.undoStackLimit());
if (!d->undoStack->isClean() && d->undoStack->undoLimit() != cfg.undoStackLimit()) {
d->undoStack->clear();
d->undoStack->setUndoLimit(cfg.undoStackLimit());
}
d->autoSaveDelay = cfg.autoSaveInterval();
setNormalAutoSaveInterval();
}
......
/*
* Copyright (c) 2019 Agata Cacko <cacko.azh@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 "KisImportExportErrorCode.h"
#include <KLocalizedString>
#include <kis_assert.h>
KisImportExportComplexError::KisImportExportComplexError(QFileDevice::FileError error) : m_error(error) { }
QString KisImportExportComplexError::qtErrorMessage() const
{
// Error descriptions in most cases taken from https://doc.qt.io/qt-5/qfiledevice.html
QString unspecifiedError = i18n("An unspecified error occurred.");
switch (m_error) {
case QFileDevice::FileError::NoError :
// Returning this file error may mean that something is wrong in our code.
// Successful operation should return ImportExportCodes::OK instead.
return i18n("The action has been completed successfully.");
case QFileDevice::FileError::ReadError :
return i18n("An error occurred when reading from the file.");
case QFileDevice::FileError::WriteError :
return i18n("An error occurred when writing to the file.");
case QFileDevice::FileError::FatalError :
return i18n("A fatal error occurred.");
case QFileDevice::FileError::ResourceError :
return i18n("Out of resources (e.g. out of memory).");
case QFileDevice::FileError::OpenError :
return i18n("The file could not be opened.");
case QFileDevice::FileError::AbortError :
return i18n("The operation was aborted.");
case QFileDevice::FileError::TimeOutError :
return i18n("A timeout occurred.");
case QFileDevice::FileError::UnspecifiedError :
return unspecifiedError;
case QFileDevice::FileError::RemoveError :
return i18n("The file could not be removed.");
case QFileDevice::FileError::RenameError :
return i18n("The file could not be renamed.");
case QFileDevice::FileError::PositionError :
return i18n("The position in the file could not be changed.");
case QFileDevice::FileError::ResizeError :
return i18n("The file could not be resized.");
case QFileDevice::FileError::PermissionsError :
return i18n("Permission denied. Krita is not allowed to read or write to the file.");
case QFileDevice::FileError::CopyError :
return i18n("The file could not be copied.");
}
return unspecifiedError;
}
KisImportExportErrorCannotRead::KisImportExportErrorCannotRead(QFileDevice::FileError error) : KisImportExportComplexError(error) {
KIS_ASSERT_RECOVER_NOOP(error != QFileDevice::NoError);
}
QString KisImportExportErrorCannotRead::errorMessage() const
{
return i18n("Cannot open file for reading. Reason: %1", qtErrorMessage());
}
KisImportExportErrorCannotWrite::KisImportExportErrorCannotWrite(QFileDevice::FileError error) : KisImportExportComplexError(error) {
KIS_ASSERT_RECOVER_NOOP(error != QFileDevice::NoError);
}
QString KisImportExportErrorCannotWrite::errorMessage() const
{
return i18n("Cannot open file for writing. Reason: %1", qtErrorMessage());
}
KisImportExportErrorCode::KisImportExportErrorCode() : errorFieldUsed(None), cannotRead(QFileDevice::FileError()), cannotWrite(QFileDevice::FileError()) { }
KisImportExportErrorCode::KisImportExportErrorCode(ImportExportCodes::ErrorCodeID id) : errorFieldUsed(CodeId), codeId(id), cannotRead(QFileDevice::FileError()), cannotWrite(QFileDevice::FileError()) { }
KisImportExportErrorCode::KisImportExportErrorCode(KisImportExportErrorCannotRead error) : errorFieldUsed(CannotRead), cannotRead(error), cannotWrite(QFileDevice::FileError()) { }
KisImportExportErrorCode::KisImportExportErrorCode(KisImportExportErrorCannotWrite error) : errorFieldUsed(CannotWrite), cannotRead(QFileDevice::FileError()), cannotWrite(error) { }
bool KisImportExportErrorCode::isOk() const
{
// if cannotRead or cannotWrite is "NoError", it means that something is wrong in our code
return errorFieldUsed == CodeId && codeId == ImportExportCodes::OK;
}
bool KisImportExportErrorCode::isCancelled() const
{
return errorFieldUsed == CodeId && codeId == ImportExportCodes::Cancelled;
}
bool KisImportExportErrorCode::isInternalError() const
{
return errorFieldUsed == CodeId && codeId == ImportExportCodes::InternalError;
}
QString KisImportExportErrorCode::errorMessage() const
{
QString internal = i18n("Unexpected error. Please contact developers.");
if (errorFieldUsed == CannotRead) {
return cannotRead.errorMessage();
} else if (errorFieldUsed == CannotWrite) {
return cannotWrite.errorMessage();
} else if (errorFieldUsed == CodeId) {
switch (codeId) {
// Reading
case ImportExportCodes::FileNotExist:
return i18n("The file doesn't exists.");
case ImportExportCodes::NoAccessToRead:
return i18n("Permission denied: Krita is not allowed to read the file.");
case ImportExportCodes::FileFormatIncorrect:
return i18n("The file format cannot be parsed.");
case ImportExportCodes::FormatFeaturesUnsupported:
return i18n("The file format contains unsupported features.");
case ImportExportCodes::FormatColorSpaceUnsupported:
return i18n("The file format contains unsupported color space.");
// Writing
case ImportExportCodes::CannotCreateFile:
return i18n("The file cannot be created.");
case ImportExportCodes::NoAccessToWrite:
return i18n("Permission denied: Krita is not allowed to write to the file.");
case ImportExportCodes::InsufficientMemory:
return i18n("There is not enough memory left to save the file.");
// Both
case ImportExportCodes::Cancelled:
return i18n("The action was cancelled by the user.");
// Other
case ImportExportCodes::Failure:
return i18n("Unknown error.");
case ImportExportCodes::InternalError:
return internal;
// OK
case ImportExportCodes::OK:
return i18n("The action has been completed successfully.");
default:
return internal;
}
}
return internal; // errorFieldUsed = None
}
QDebug operator<<(QDebug d, const KisImportExportErrorCode& errorCode)
{
switch(errorCode.errorFieldUsed) {
case KisImportExportErrorCode::None:
d << "None of the error fields is in use.";
break;
case KisImportExportErrorCode::CannotRead:
d << "Cannot read: " << errorCode.cannotRead.m_error;
break;
case KisImportExportErrorCode::CannotWrite:
d << "Cannot read: " << errorCode.cannotRead.m_error;
break;
case KisImportExportErrorCode::CodeId:
d << "Error code = " << errorCode.codeId;
}
d << " " << errorCode.errorMessage();
return d;
}
/*
* Copyright (c) 2019 Agata Cacko <cacko.azh@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_IMPORT_EXPORT_ERROR_CODES_H
#define KIS_IMPORT_EXPORT_ERROR_CODES_H
#include <QFile>
#include <QFileDevice>
#include <QString>
#include <kritaui_export.h>
#include <QDebug>
namespace ImportExportCodes
{
enum KRITAUI_EXPORT ErrorCodeID
{
InternalError, // error that shouldn't happen; only inside ASSERTS
// Reading
FileNotExist, // there is no file with that name in that location,
NoAccessToRead, // Krita has no reading access to the file,
ErrorWhileReading, // there was an error that occured during reading,
FileFormatIncorrect, // file format cannot be parsed,
FormatFeaturesUnsupported, // file format can be parsed, but some features are unsupported,
FormatColorSpaceUnsupported, // file format can be parsed, but color space of the image is unsupported
// Writing
CannotCreateFile, // file cannot be created
NoAccessToWrite, // Krita has no writing access to the file
ErrorWhileWriting, // there was an error that occured during writing (can be insufficient memory, too, just we don't know)
InsufficientMemory, // there is not enough memory left
// Both
Cancelled, // cancelled by a user
// Other
Failure, // unspecified error
// OK
OK, // everything went ok
};
};
struct KisImportExportErrorCode;
struct KRITAUI_EXPORT KisImportExportComplexError
{
virtual QString errorMessage() const = 0;
KisImportExportComplexError(QFileDevice::FileError error);
friend QDebug operator<<(QDebug d, const KisImportExportErrorCode& errorCode);
protected:
QString qtErrorMessage() const;
QFileDevice::FileError m_error;
virtual ~KisImportExportComplexError() {}
};
struct KRITAUI_EXPORT KisImportExportErrorCannotWrite : KisImportExportComplexError
{
KisImportExportErrorCannotWrite(QFileDevice::FileError error);
QString errorMessage() const override;
~KisImportExportErrorCannotWrite() { }
};
struct KRITAUI_EXPORT KisImportExportErrorCannotRead : KisImportExportComplexError
{
KisImportExportErrorCannotRead(QFileDevice::FileError error);
QString errorMessage() const override;
~KisImportExportErrorCannotRead() { }
};
struct KRITAUI_EXPORT KisImportExportErrorCode
{
public:
// required by kis_async_action_feedback
KisImportExportErrorCode();
KisImportExportErrorCode(ImportExportCodes::ErrorCodeID code);
KisImportExportErrorCode(KisImportExportErrorCannotRead code);
KisImportExportErrorCode(KisImportExportErrorCannotWrite code);
QString errorMessage() const;
bool isOk() const;
bool isCancelled() const;
bool isInternalError() const;
friend QDebug operator<<(QDebug d, const KisImportExportErrorCode& errorCode);
private:
enum ErrorFieldUsed
{
None,
CodeId,
CannotRead,
CannotWrite
};
ErrorFieldUsed errorFieldUsed;
ImportExportCodes::ErrorCodeID codeId;
KisImportExportErrorCannotRead cannotRead;
KisImportExportErrorCannotWrite cannotWrite;
};
KRITAUI_EXPORT QDebug operator<<(QDebug d, const KisImportExportErrorCode& errorCode);
#endif // KIS_IMPORT_EXPORT_ERROR_CODES_H
This diff is collapsed.
......@@ -459,7 +459,7 @@ private:
/**
* Updates the window caption based on the document info and path.
*/
void updateCaption(const QString & caption, bool mod);
void updateCaption(const QString & caption, bool modified);
void updateReloadFileAction(KisDocument *doc);
void saveWindowSettings();
......
......@@ -1056,20 +1056,13 @@ void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
{
QPointF offsetBefore = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
qreal devicePixelRatio = m_d->coordinatesConverter->devicePixelRatio();
// The given offset is in widget logical pixels. In order to prevent fuzzy
// canvas rendering at 100% pixel-perfect zoom level when devicePixelRatio
// is not integral, we adjusts the offset to map to whole device pixels.
// We use qFloor here since the offset can be negative.
int deviceOffsetX = qFloor(documentOffset.x() * devicePixelRatio);
int deviceOffsetY = qFloor(documentOffset.y() * devicePixelRatio);
// These adjusted offsets will be in logical pixel but is aligned in device
// pixel space for pixel-perfect rendering.
qreal pixelPerfectOffsetX = deviceOffsetX / devicePixelRatio;
qreal pixelPerfectOffsetY = deviceOffsetY / devicePixelRatio;
//
// FIXME: This is a temporary hack for fixing the canvas under fractional
// DPI scaling before a new coordinate system is introduced.
QPointF offsetAdjusted(pixelPerfectOffsetX, pixelPerfectOffsetY);
QPointF offsetAdjusted = m_d->coordinatesConverter->snapToDevicePixel(documentOffset);
m_d->coordinatesConverter->setDocumentOffset(offsetAdjusted);
QPointF offsetAfter = m_d->coordinatesConverter->imageRectInViewportPixels().topLeft();
......
......@@ -261,12 +261,6 @@ void KisCanvasController::slotToggleWrapAroundMode(bool value)
m_d->view->viewManager()->showFloatingMessage(i18n("You are activating wrap-around mode, but have not enabled OpenGL.\n"
"To visualize wrap-around mode, enable OpenGL."), QIcon());
}
else if (value) {
QAction *action = m_d->view->viewManager()->actionCollection()->action("wrap_around_mode");
QString shortcut = action ? action->shortcut().toString() : "W";
m_d->view->viewManager()->showFloatingMessage(i18n("Entering Wraparound mode. Press '%1' to leave Wraparound mode.", shortcut), QIcon());
}
kritaCanvas->setWrapAroundViewingMode(value);
kritaCanvas->image()->setWrapAroundModePermitted(value);
}
......
......@@ -21,6 +21,7 @@
#include "kis_coordinates_converter.h"
#include <QtMath>
#include <QTransform>
#include <KoViewConverter.h>
......@@ -467,3 +468,19 @@ void KisCoordinatesConverter::imagePhysicalScale(qreal *scaleX, qreal *scaleY) c
*scaleX *= m_d->devicePixelRatio;
*scaleY *= m_d->devicePixelRatio;
}
/**
* @brief Adjust a given pair of coordinates to the nearest device pixel
* according to the value of `devicePixelRatio`.
* @param point a point in logical pixel space
* @return The point in logical pixel space but adjusted to the nearest device
* pixel
*/
QPointF KisCoordinatesConverter::snapToDevicePixel(const QPointF &point) const
{
QPoint devicePixel = (point * m_d->devicePixelRatio).toPoint();
// These adjusted coords will be in logical pixel but is aligned in device
// pixel space for pixel-perfect rendering.
return QPointF(devicePixel) / m_d->devicePixelRatio;
}
......@@ -151,6 +151,8 @@ public:
void imageScale(qreal *scaleX, qreal *scaleY) const;
void imagePhysicalScale(qreal *scaleX, qreal *scaleY) const;
QPointF snapToDevicePixel(const QPointF &point) const;
private:
friend class KisZoomAndPanTest;
......
......@@ -162,8 +162,8 @@ GeneralTab::GeneralTab(QWidget *_parent, const char *_name)
connect(m_bnFileName, SIGNAL(clicked()), SLOT(getBackgroundImage()));
connect(clearBgImageButton, SIGNAL(clicked()), SLOT(clearBackgroundImage()));
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor());
QString xml = cfg.getMDIBackgroundColor();
KoColor mdiColor = KoColor::fromXML(xml);
m_mdiColor->setColor(mdiColor);
m_chkRubberBand->setChecked(cfg.readEntry<int>("mdi_rubberband", cfg.useOpenGL()));
......@@ -283,7 +283,7 @@ void GeneralTab::setDefault()
m_chkRubberBand->setChecked(cfg.useOpenGL(true));
m_favoritePresetsSpinBox->setValue(cfg.favoritePresets(true));
KoColor mdiColor;
mdiColor.fromQColor(cfg.getMDIBackgroundColor(true));
mdiColor.fromXML(cfg.getMDIBackgroundColor(true));
m_mdiColor->setColor(mdiColor);
m_backgroundimage->setText(cfg.getMDIBackgroundImage(true));
m_chkCanvasMessages->setChecked(cfg.showCanvasMessages(true));
......@@ -1566,7 +1566,7 @@ bool KisDlgPreferences::editPreferences()
cfg.writeEntry<int>("maximumBrushSize", dialog->m_general->intMaxBrushSize->value());
cfg.writeEntry<int>("mdi_viewmode", dialog->m_general->mdiMode());
cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor());
cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toXML());
cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text());
cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval());
cfg.writeEntry("autosavefileshidden", dialog->m_general->chkHideAutosaveFiles->isChecked());
......
......@@ -212,7 +212,7 @@
<item>
<widget class="QRadioButton" name="radioWin8PointerInput">
<property name="text">
<string>Windows 8+ Pointer Input (depends on Windows Ink) (EXPERIMENTAL)</string>
<string>Windows 8+ Pointer Input (Windows Ink)</string>
</property>
</widget>
</item>
......
......@@ -54,6 +54,7 @@ KisInputConfigurationPage::KisInputConfigurationPage(QWidget *parent, Qt::Window
ui->configurationItemsArea->setSpacing(0);
ui->configurationItemsArea->addWidget(item);
}
ui->configurationItemsArea->addStretch(20); // ensures listed input are on top
QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(ui->scrollArea);
if (scroller) {
......
......@@ -11,6 +11,12 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="topMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
......
......@@ -17,6 +17,15 @@
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="topMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="descriptionLabel">
<property name="text">
......
......@@ -404,15 +404,18 @@ void KisConfig::setUseEraserBrushOpacity(bool value)
}
QColor KisConfig::getMDIBackgroundColor(bool defaultValue) const
QString KisConfig::getMDIBackgroundColor(bool defaultValue) const
{
QColor col(77, 77, 77);
return (defaultValue ? col : m_cfg.readEntry("mdiBackgroundColor", col));
KoColor kol(KoColorSpaceRegistry::instance()->rgb8());
kol.fromQColor(col);
QString xml = kol.toXML();
return (defaultValue ? xml : m_cfg.readEntry("mdiBackgroundColorXML", xml));
}
void KisConfig::setMDIBackgroundColor(const QColor &v) const
void KisConfig::setMDIBackgroundColor(const QString &v) const
{
m_cfg.writeEntry("mdiBackgroundColor", v);
m_cfg.writeEntry("mdiBackgroundColorXML", v);
}
QString KisConfig::getMDIBackgroundImage(bool defaultValue) const
......
......@@ -502,8 +502,8 @@ public:
bool useEraserBrushOpacity(bool defaultValue = false) const;
void setUseEraserBrushOpacity(bool value);
QColor getMDIBackgroundColor(bool defaultValue = false) const;
void setMDIBackgroundColor(const QColor & v) const;
QString getMDIBackgroundColor(bool defaultValue = false) const;
void setMDIBackgroundColor(const QString & v) const;
QString getMDIBackgroundImage(bool defaultValue = false) const;
void setMDIBackgroundImage(const QString & fileName) const;
......
/*
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
*
* 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 <math.h>
#include <QPainter>
#include <QPixmap>
#include <QLabel>
#include <QComboBox>
#include <QPushButton>
#include <QScrollBar>
#include <QMouseEvent>
#include <QFrame>
#include <QBrush>
#include <QLinearGradient>
#include <QImage>
#include <QPaintEvent>
#include <kis_debug.h>
#include "KoChannelInfo.h"
#include "KoBasicHistogramProducers.h"
#include "KoColorSpace.h"
#include "kis_global.h"
#include "kis_layer.h"
#include <kis_signal_compressor.h>
#include "kis_paint_device.h"
KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WindowFlags f)
: QLabel(parent, f),
m_currentDev(nullptr), m_currentProducer(nullptr),
m_smoothHistogram(false),m_histogram_type(LINEAR)
{
setObjectName(name);
}
KisHistogramView::~KisHistogramView()
{
}
KoHistogramProducer *KisHistogramView::currentProducer()
{
return m_currentProducer;
}
void KisHistogramView::startUpdateCanvasProjection()
{
updateHistogramCalculation();
}
void KisHistogramView::setChannels(QList<KoChannelInfo*> & channels)
{
m_channels = channels;
updateHistogramCalculation();
}
void KisHistogramView::setProducer(KoHistogramProducer* producer)
{
m_currentProducer = producer;
m_channels = m_currentProducer->channels();
if( !m_histogram.isNull() ){
m_histogram->setProducer( m_currentProducer );
}
updateHistogramCalculation();
}
void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer* producer, const QRect &bounds)
{
m_currentProducer = producer;
m_channels = m_currentProducer->channels();
m_currentDev = dev;
m_currentBounds = bounds;
m_histogram = new KisHistogram(m_currentDev, m_currentBounds, m_currentProducer, m_histogram_type);