diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphicShape.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphicShape.cpp index e731d3892b..d1eb18fd25 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphicShape.cpp +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphicShape.cpp @@ -1,576 +1,570 @@ /* This file is part of the KDE project Copyright (C) 2008 Fela Winkelmolen 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 "KarbonCalligraphicShape.h" #include #include #include "KarbonSimplifyPath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #undef M_PI const qreal M_PI = 3.1415927; KarbonCalligraphicShape::KarbonCalligraphicShape(KisPropertiesConfigurationSP settings) : m_lastWasFlip(false) , m_strokeConfig(settings) { setShapeId(KoPathShapeId); setFillRule(Qt::WindingFill); setBackground(QSharedPointer(new KoColorBackground(QColor(Qt::black)))); setStroke(KoShapeStrokeModelSP()); m_sizeOption.readOptionSetting(settings); m_rotationOption.readOptionSetting(settings); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); } KarbonCalligraphicShape::KarbonCalligraphicShape(const KarbonCalligraphicShape &rhs) : KoParameterShape(new KoParameterShapePrivate(*rhs.d_func(), this)), m_points(rhs.m_points), m_lastWasFlip(rhs.m_lastWasFlip), m_strokeConfig(rhs.m_strokeConfig) { } KarbonCalligraphicShape::~KarbonCalligraphicShape() { } KisPropertiesConfigurationSP KarbonCalligraphicShape::configuration() const { return m_strokeConfig; } void KarbonCalligraphicShape::setConfiguration(KisPropertiesConfigurationSP setting) { m_strokeConfig = setting; m_sizeOption = KisPressureSizeOption(); m_sizeOption.readOptionSetting(setting); m_rotationOption = KisPressureRotationOption(); m_rotationOption.readOptionSetting(setting); m_sizeOption.resetAllSensors(); m_rotationOption.resetAllSensors(); m_rotationOption.setChecked(true); m_sizeOption.setChecked(true); updatePath(this->size()); } KoShape *KarbonCalligraphicShape::cloneShape() const { return new KarbonCalligraphicShape(*this); } void KarbonCalligraphicShape::appendPoint(KisPaintInformation &paintInfo) { // convert the point from canvas to shape coordinates paintInfo.setPos(paintInfo.pos()-position()); KarbonCalligraphicPoint *calligraphicPoint = new KarbonCalligraphicPoint(paintInfo); QList handles = this->handles(); handles.append(paintInfo.pos()); setHandles(handles); m_points.append(calligraphicPoint); if (m_points.count()<2) { appendPointToPath(m_points.count()-1); } else { updatePath(QSize()); } } void KarbonCalligraphicShape::appendPointToPath(int index) { KarbonCalligraphicPoint *p = m_points.at(index); qreal width = calculateWidth(p->paintInfo()); qreal angle = calculateAngle(p->paintInfo()); qreal dx = std::cos(angle) * width; qreal dy = std::sin(angle) * width; // find the outline points QPointF p1 = p->point() - QPointF(dx / 2, dy / 2); QPointF p2 = p->point() + QPointF(dx / 2, dy / 2); if (pointCount() == 0) { moveTo(p1); lineTo(p2); normalize(); return; } // pointCount > 0 bool flip = false; if (m_points.count()>2 && m_points.count()>index+1) { QLineF line1(m_points.at(index-1)->point(), m_points.at(index)->point()); QLineF line2(m_points.at(index)->point(), m_points.at(index+1)->point()); qreal diffAngle = line1.angle(line2); if (diffAngle>90.0 && diffAngle<270.0) { flip = true; } } // if there was a flip add additional points if (flip) { qreal pm1width = calculateWidth(m_points.at(index-1)->paintInfo()); qreal pm1angle = calculateAngle(m_points.at(index-1)->paintInfo()); QPointF pm1vec = QPointF(cos(pm1angle)*pm1width / 2, sin(pm1angle)*pm1width / 2); QPointF p1m1 = m_points.at(index-1)->point() - pm1vec; QPointF p2m1 = m_points.at(index-1)->point() + pm1vec; qreal pp1width = calculateWidth(m_points.at(index+1)->paintInfo()); qreal pp1angle = calculateAngle(m_points.at(index+1)->paintInfo()); QPointF pp1vec = QPointF(cos(pp1angle)*pp1width / 2, sin(pp1angle)*pp1width / 2); QPointF p1p1 = m_points.at(index+1)->point() - pp1vec; QPointF p2p1 = m_points.at(index+1)->point() + pp1vec; QPointF intersect; if (QLineF(p1, p1p1).intersect(QLineF(p2m1, p2), &intersect) == QLineF::BoundedIntersection) { appendPointsToPathAux(p1, intersect); appendPointsToPathAux(p2, intersect); } else if (QLineF(p1m1, p1).intersect(QLineF(p2, p2p1), &intersect) == QLineF::BoundedIntersection) { appendPointsToPathAux(intersect, p2); appendPointsToPathAux(intersect, p1); } else { appendPointsToPathAux(p1, p2); } } else { appendPointsToPathAux(p1, p2); } if (pointCount() > 4) { smoothLastPoints(); if (flip) { int index = pointCount() / 2; // find the last two points KoPathPoint *last1 = pointByIndex(KoPathPointIndex(0, index - 1)); KoPathPoint *last2 = pointByIndex(KoPathPointIndex(0, index)); last1->removeControlPoint1(); last1->removeControlPoint2(); last2->removeControlPoint1(); last2->removeControlPoint2(); m_lastWasFlip = true; } if (m_lastWasFlip) { int index = pointCount() / 2; // find the previous two points KoPathPoint *prev1 = pointByIndex(KoPathPointIndex(0, index - 2)); KoPathPoint *prev2 = pointByIndex(KoPathPointIndex(0, index + 1)); prev1->removeControlPoint1(); prev1->removeControlPoint2(); prev2->removeControlPoint1(); prev2->removeControlPoint2(); if (!flip) { m_lastWasFlip = false; } } } normalize(); // add initial cap if it's the fourth added point // this code is here because this function is called from different places // pointCount() == 8 may causes crashes because it doesn't take possible // flips into account if (m_points.count() >= 4 && p == m_points[3] && configuration()->getFloat("capSize")>0) { addCap(3, 0, 0, true); // duplicate the last point to make the points remain "balanced" // needed to keep all indexes code (else I would need to change // everything in the code...) KoPathPoint *last = pointByIndex(KoPathPointIndex(0, pointCount() - 1)); KoPathPoint *newPoint = new KoPathPoint(this, last->point()); insertPoint(newPoint, KoPathPointIndex(0, pointCount())); close(); } } void KarbonCalligraphicShape::appendPointsToPathAux(const QPointF &p1, const QPointF &p2) { KoPathPoint *pathPoint1 = new KoPathPoint(this, p1); KoPathPoint *pathPoint2 = new KoPathPoint(this, p2); // calculate the index of the insertion position int index = pointCount() / 2; insertPoint(pathPoint2, KoPathPointIndex(0, index)); insertPoint(pathPoint1, KoPathPointIndex(0, index)); } KarbonCalligraphicPoint *KarbonCalligraphicShape::lastPoint() { return m_points.last(); } void KarbonCalligraphicShape::smoothLastPoints() { int index = pointCount() / 2; smoothPoint(index - 2); smoothPoint(index + 1); } void KarbonCalligraphicShape::smoothPoint(const int index) { if (pointCount() < index + 2) { return; } else if (index < 1) { return; } const KoPathPointIndex PREV(0, index - 1); const KoPathPointIndex INDEX(0, index); const KoPathPointIndex NEXT(0, index + 1); QPointF prev = pointByIndex(PREV)->point(); QPointF point = pointByIndex(INDEX)->point(); QPointF next = pointByIndex(NEXT)->point(); QPointF vector = next - prev; qreal dist = (QLineF(prev, next)).length(); // normalize the vector (make it's size equal to 1) if (!qFuzzyCompare(dist + 1, 1)) { vector /= dist; } qreal mult = 0.35; // found by trial and error, might not be perfect... // distance of the control points from the point qreal dist1 = (QLineF(point, prev)).length() * mult; qreal dist2 = (QLineF(point, next)).length() * mult; QPointF vector1 = vector * dist1; QPointF vector2 = vector * dist2; QPointF controlPoint1 = point - vector1; QPointF controlPoint2 = point + vector2; pointByIndex(INDEX)->setControlPoint1(controlPoint1); pointByIndex(INDEX)->setControlPoint2(controlPoint2); } const QRectF KarbonCalligraphicShape::lastPieceBoundingRect() { if (pointCount() < 6) { return QRectF(); } int index = pointCount() / 2; QPointF p1 = pointByIndex(KoPathPointIndex(0, index - 3))->point(); QPointF p2 = pointByIndex(KoPathPointIndex(0, index - 2))->point(); QPointF p3 = pointByIndex(KoPathPointIndex(0, index - 1))->point(); QPointF p4 = pointByIndex(KoPathPointIndex(0, index))->point(); QPointF p5 = pointByIndex(KoPathPointIndex(0, index + 1))->point(); QPointF p6 = pointByIndex(KoPathPointIndex(0, index + 2))->point(); // TODO: also take the control points into account QPainterPath p; p.moveTo(p1); p.lineTo(p2); p.lineTo(p3); p.lineTo(p4); p.lineTo(p5); p.lineTo(p6); return p.boundingRect().translated(position()); } bool KarbonCalligraphicShape::flipDetected(const QPointF &p1, const QPointF &p2) { // detect the flip caused by the angle changing 180 degrees // thus detect the boundary crossing int index = pointCount() / 2; QPointF last1 = pointByIndex(KoPathPointIndex(0, index - 1))->point(); QPointF last2 = pointByIndex(KoPathPointIndex(0, index))->point(); int sum1 = std::abs(ccw(p1, p2, last1) + ccw(p1, last2, last1)); int sum2 = std::abs(ccw(p2, p1, last2) + ccw(p2, last1, last2)); // if there was a flip return sum1 < 2 && sum2 < 2; } int KarbonCalligraphicShape::ccw(const QPointF &p1, const QPointF &p2,const QPointF &p3) { // calculate two times the area of the triangle fomed by the points given qreal area2 = (p2.x() - p1.x()) * (p3.y() - p1.y()) - (p2.y() - p1.y()) * (p3.x() - p1.x()); if (area2 > 0) { return +1; // the points are given in counterclockwise order } else if (area2 < 0) { return -1; // the points are given in clockwise order } else { return 0; // the points form a degenerate triangle } } void KarbonCalligraphicShape::setSize(const QSizeF &newSize) { // QSizeF oldSize = size(); // TODO: check KoParameterShape::setSize(newSize); } QPointF KarbonCalligraphicShape::normalize() { QPointF offset(KoParameterShape::normalize()); QTransform matrix; matrix.translate(-offset.x(), -offset.y()); for (int i = 0; i < m_points.size(); ++i) { m_points[i]->setPoint(matrix.map(m_points[i]->point())); } m_lastOffset = offset; return offset; } void KarbonCalligraphicShape::moveHandleAction(int handleId, const QPointF &point, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); m_points[handleId]->setPoint(point); } void KarbonCalligraphicShape::updatePath(const QSizeF &size) { Q_UNUSED(size); // remove all points clear(); //KarbonCalligraphicPoint *pLast = m_points.at(0); m_strokeDistance = new KisDistanceInformation(QPoint(), 0.0); for (int i=0; i< m_points.count(); i++) { KarbonCalligraphicPoint *p = m_points.at(i); { KisPaintInformation::DistanceInformationRegistrar r = p->paintInfo()->registerDistanceInformation(m_strokeDistance); // NOTE: only in this scope you can use all the methods of the painting information, including drawingAngle(), distance and speed. appendPointToPath(i); } // after the point is "painter" it should be added to the distance information as the "previous" point m_strokeDistance->registerPaintedDab(*p->paintInfo(), KisSpacingInformation(1.0)); } simplifyPath(); QList handles; Q_FOREACH (KarbonCalligraphicPoint *p, m_points) { handles.append(p->point()); } setHandles(handles); } void KarbonCalligraphicShape::simplifyPath() { if (m_points.count() < 2) { return; } close(); // add final cap addCap(m_points.count() - 2, m_points.count() - 1, pointCount() / 2); // TODO: the error should be proportional to the width // and it shouldn't be a magic number karbonSimplifyPath(this, 0.3); } void KarbonCalligraphicShape::addCap(int index1, int index2, int pointIndex, bool inverted) { QPointF p1 = m_points[index1]->point(); QPointF p2 = m_points[index2]->point(); // TODO: review why spikes can appear with a lower limit QPointF delta = p2 - p1; if (delta.manhattanLength() < 1.0) { return; } QPointF direction = QLineF(QPointF(0, 0), delta).unitVector().p2(); qreal width = calculateWidth(m_points[index2]->paintInfo()); qreal capSize = configuration()->getFloat("capSize"); QPointF p = p2 + direction * capSize * width; KoPathPoint *newPoint = new KoPathPoint(this, p); qreal angle = calculateAngle(m_points[index2]->paintInfo()); if (inverted) { angle += M_PI; } qreal dx = std::cos(angle) * width; qreal dy = std::sin(angle) * width; newPoint->setControlPoint1(QPointF(p.x() - dx / 2, p.y() - dy / 2)); newPoint->setControlPoint2(QPointF(p.x() + dx / 2, p.y() + dy / 2)); insertPoint(newPoint, KoPathPointIndex(0, pointIndex)); } qreal KarbonCalligraphicShape::calculateWidth(KisPaintInformation *p) { if (m_sizeOption.isCurveUsed()) { return m_sizeOption.apply(*p)*configuration()->getDouble("strokeWidth", 10); } return configuration()->getDouble("strokeWidth", 10); } qreal KarbonCalligraphicShape::calculateAngle(KisPaintInformation *p) { if (m_rotationOption.isCurveUsed()) { return (2*M_PI)-m_rotationOption.apply(*p); } return 0; } QString KarbonCalligraphicShape::pathShapeId() const { return KarbonCalligraphicShapeId; } bool KarbonCalligraphicShape::saveSvg(SvgSavingContext &context) { context.shapeWriter().startElement("path"); context.shapeWriter().addAttribute("krita:type", "calligraphic-stroke"); context.shapeWriter().addAttribute("id", context.getID(this)); context.shapeWriter().addAttribute("transform", SvgUtil::transformToString(transformation())); context.shapeWriter().addAttribute("d", this->toString(context.userSpaceTransform())); SvgStyleWriter::saveSvgStyle(this, context); QDomDocument doc= QDomDocument(); QDomElement baseNode = doc.createElement("krita:calligraphic-stroke-data"); baseNode.setAttribute("xmlns", "http://krita.org/kritasvg"); Q_FOREACH (KarbonCalligraphicPoint *p, m_points) { QDomElement infoElt = doc.createElement("point"); p->paintInfo()->toXML(doc, infoElt); baseNode.appendChild(infoElt); } QDomElement configElt = doc.createElement("config"); configuration()->toXML(doc, configElt); baseNode.appendChild(configElt); doc.appendChild(baseNode); context.shapeWriter().addCompleteElement(doc.toString().toUtf8()); context.shapeWriter().endElement(); return true; } bool KarbonCalligraphicShape::loadSvg(const KoXmlElement &element, SvgLoadingContext &context) { Q_UNUSED(context); const QString extendedNamespace = element.attribute("krita:type"); if (element.tagName() == "path" && !extendedNamespace.isEmpty()) { QDomDocument doc = QDomDocument(); KoXml::asQDomElement(doc, element); QDomElement root = doc.firstChildElement("path").firstChildElement("krita:calligraphic-stroke-data"); QDomElement configElt = root.firstChildElement("config"); KisPropertiesConfigurationSP config(new KisPropertiesConfiguration()); - //attempt at debugging this... - for(int i=0;i< configElt.childNodes().count(); i++) { - qDebug()<fromXML(configElt); QDomElement infoElt = root.firstChildElement("point"); while (!infoElt.isNull()) { KisPaintInformation paintInfo = KisPaintInformation::fromXML(infoElt); m_points.append(new KarbonCalligraphicPoint(paintInfo)); infoElt = infoElt.nextSiblingElement("point"); } setConfiguration(config); return true; } return false; } void KarbonCalligraphicShape::simplifyGuidePath() { // do not attempt to simplify if there are too few points if (m_points.count() < 3) { return; } QList points; Q_FOREACH (KarbonCalligraphicPoint *p, m_points) { points.append(p->point()); } // cumulative data used to determine if the point can be removed qreal widthChange = 0; qreal directionChange = 0; QList::iterator i = m_points.begin() + 2; while (i != m_points.end() - 1) { QPointF point = (*i)->point(); qreal width = calculateWidth((*i)->paintInfo()); qreal prevWidth = calculateWidth((*(i - 1))->paintInfo()); qreal widthDiff = width - prevWidth; widthDiff /= qMax(width, prevWidth); qreal directionDiff = 0; if ((i + 1) != m_points.end()) { QPointF prev = (*(i - 1))->point(); QPointF next = (*(i + 1))->point(); directionDiff = QLineF(prev, point).angleTo(QLineF(point, next)); if (directionDiff > 180) { directionDiff -= 360; } } if (directionChange * directionDiff >= 0 && qAbs(directionChange + directionDiff) < 20 && widthChange * widthDiff >= 0 && qAbs(widthChange + widthDiff) < 0.1) { // deleted point //(*i)->paintInfo(); delete *i; i = m_points.erase(i); directionChange += directionDiff; widthChange += widthDiff; } else { // keep point directionChange = 0; widthChange = 0; ++i; } } updatePath(QSizeF()); } diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.cpp b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.cpp index 5842b96e12..b12d5f2707 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.cpp +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.cpp @@ -1,475 +1,548 @@ /* This file is part of the KDE project Copyright (C) 2008 Fela Winkelmolen 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 "KarbonCalligraphyOptionWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_double_parse_spin_box.h" #include "kis_int_parse_spin_box.h" #include "ui_karboncalligraphytooloptions.h" #include "kis_slider_spin_box.h" #include #include #include #include /* Profiles are saved in karboncalligraphyrc In the group "General", profile is the name of profile used Every profile is described in a group, the name of which is "ProfileN" Starting to count from 0 onwards (NOTE: the index in profiles is different from the N) Default profiles are added by the function addDefaultProfiles(), once they have been added, the entry defaultProfilesAdded in the "General" group is set to true TODO: add a reset defaults option? */ // name of the configuration file const QString RCFILENAME = "karboncalligraphyrc"; class KarbonCalligraphyToolOptions: public QWidget, public Ui::WdgCalligraphyToolOptions { public: KarbonCalligraphyToolOptions(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; KarbonCalligraphyOptionWidget::KarbonCalligraphyOptionWidget() : m_changingProfile(false) { m_options = new KarbonCalligraphyToolOptions(); m_options->setupUi(this); m_options->sldDistanceInterval->setPrefix(i18n("Distance: ")); //the distance is in SCREEN coordinates! m_options->sldDistanceInterval->setSuffix(i18n("px")); m_options->sldDistanceInterval->setRange(1, 1000, 2); m_options->sldDistanceInterval->setSingleStep(1); m_options->sldTimeInterval->setPrefix(i18n("Time: ")); m_options->sldTimeInterval->setSuffix(i18n("ms")); m_options->sldTimeInterval->setRange(1, 1000, 2); m_options->sldTimeInterval->setSingleStep(1); m_options->sldCaps->setPrefix(i18n("Caps: ")); m_options->sldCaps->setRange(0.0, 2.0, 2); m_options->sldCaps->setSingleStep(0.03); m_sizeOption = new KisCurveOptionWidget(new KisPressureSizeOption(), i18n("0%"), i18n("100%")); m_rotationOption = new KisCurveOptionWidget(new KisPressureRotationOption(), i18n("-180°"), i18n("180°")); //m_ratioOption = new KisCurveOptionWidget(new KisPressureRatioOption(), i18n("0%"), i18n("100%")); m_rotationOption->setChecked(true); m_sizeOption->setChecked(true); m_options->bnSize->setIcon(kisIcon("brush_size")); m_options->bnSize->setPopupWidget(m_sizeOption->configurationPage()); m_options->bnRotation->setIcon(kisIcon("brush_rotation")); m_options->bnRotation->setPopupWidget(m_rotationOption->configurationPage()); m_options->bnRatio->hide(); //m_options->bnRatio->setIcon(kisIcon("brush_ratio")); //m_options->bnRatio->setPopupWidget(m_ratioOption->configurationPage()); createConnections(); addDefaultProfiles(); // if they are already added does nothing loadProfiles(); } KarbonCalligraphyOptionWidget::~KarbonCalligraphyOptionWidget() { qDeleteAll(m_profiles); } void KarbonCalligraphyOptionWidget::emitAll() { emit usePathChanged(m_options->rdAdjustPath->isChecked()); - + emit useAssistantChanged(m_options->rdAdjustAssistant->isChecked()); + emit useNoAdjustChanged(m_options->rdNoAdjust->isChecked()); + emit generateSettings(); emit smoothTimeChanged(m_options->sldTimeInterval->value()); emit smoothDistanceChanged(m_options->sldDistanceInterval->value()); } void KarbonCalligraphyOptionWidget::loadProfile(const QString &name) { if (m_changingProfile) { return; } // write the new profile in the config file KConfig config(RCFILENAME); KConfigGroup generalGroup(&config, "General"); generalGroup.writeEntry("profile", name); config.sync(); // and load it loadCurrentProfile(); // don't show Current if it isn't selected if (name != i18n("Current")) { removeProfile(i18n("Current")); } } void KarbonCalligraphyOptionWidget::updateCurrentProfile() { if (!m_changingProfile) { saveProfile("Current"); } } void KarbonCalligraphyOptionWidget::saveProfileAs() { QString name; // loop until a valid name is entered or the user cancelled while (1) { bool ok; name = QInputDialog::getText(this, i18n("Profile name"), i18n("Please insert the name by which " "you want to save this profile:"), QLineEdit::Normal, QString(), &ok); if (!ok) { return; } if (name.isEmpty() || name == i18n("Current")) { KMessageBox::sorry(this, i18n("Sorry, the name you entered is invalid."), i18nc("invalid profile name", "Invalid name.")); // try again saveProfileAs(); continue; // ask again } if (m_profiles.contains(name)) { int ret = KMessageBox::warningYesNo(this, i18n("A profile with that name already exists.\n" "Do you want to overwrite it?")); if (ret == KMessageBox::Yes) { break; // exit while loop (save profile) } // else ask again } else { // the name is valid break; // exit while loop (save profile) } } saveProfile(name); } void KarbonCalligraphyOptionWidget::removeProfile() { removeProfile(m_options->cmbProfiles->currentText()); } void KarbonCalligraphyOptionWidget::generateSettings() { KisPropertiesConfigurationSP settings = new KisPropertiesConfiguration(); settings->setProperty("capSize", m_options->sldCaps->value()); m_sizeOption->writeOptionSetting(settings); m_rotationOption->writeOptionSetting(settings); emit settingsChanged(settings); } /****************************************************************************** ************************* Convenience Functions ****************************** ******************************************************************************/ void KarbonCalligraphyOptionWidget::createConnections() { connect(m_options->cmbProfiles, SIGNAL(currentIndexChanged(QString)), SLOT(loadProfile(QString))); // propagate changes connect(m_options->rdAdjustPath, SIGNAL(toggled(bool)), SIGNAL(usePathChanged(bool))); connect(m_options->rdAdjustAssistant, SIGNAL(toggled(bool)), SIGNAL(useAssistantChanged(bool))); connect(m_options->rdNoAdjust, SIGNAL(toggled(bool)), SIGNAL(useNoAdjustChanged(bool))); connect(m_options->sldCaps, SIGNAL(valueChanged(double)), SLOT(generateSettings())); connect(m_options->sldTimeInterval, SIGNAL(valueChanged(double)), SIGNAL(smoothTimeChanged(double))); connect(m_options->sldDistanceInterval, SIGNAL(valueChanged(double)), SIGNAL(smoothDistanceChanged(double))); connect(m_sizeOption, SIGNAL(sigSettingChanged()), SLOT(generateSettings())); connect(m_rotationOption, SIGNAL(sigSettingChanged()), SLOT(generateSettings())); // update profile connect(m_options->rdAdjustPath, SIGNAL(toggled(bool)), SLOT(updateCurrentProfile())); connect(m_options->rdAdjustAssistant, SIGNAL(toggled(bool)), SLOT(updateCurrentProfile())); connect(m_options->rdNoAdjust, SIGNAL(toggled(bool)), SLOT(updateCurrentProfile())); connect(m_options->sldCaps, SIGNAL(valueChanged(double)), SLOT(updateCurrentProfile())); connect(m_options->sldTimeInterval, SIGNAL(valueChanged(double)), SLOT(updateCurrentProfile())); connect(m_options->sldDistanceInterval, SIGNAL(valueChanged(double)), SLOT(updateCurrentProfile())); + connect(m_sizeOption, SIGNAL(sigSettingChanged()), SLOT(updateCurrentProfile())); + connect(m_rotationOption, SIGNAL(sigSettingChanged()), SLOT(updateCurrentProfile())); connect(m_options->bnSaveProfile, SIGNAL(clicked()), SLOT(saveProfileAs())); connect(m_options->bnRemoveProfile, SIGNAL(clicked()), SLOT(removeProfile())); // visualization //connect(m_useAngle, SIGNAL(toggled(bool)), SLOT(toggleUseAngle(bool))); } void KarbonCalligraphyOptionWidget::addDefaultProfiles() { // check if the profiles where already added KConfig config(RCFILENAME); KConfigGroup generalGroup(&config, "General"); if (generalGroup.readEntry("defaultProfilesAdded", false)) { return; } KConfigGroup profile0(&config, "Profile0"); profile0.writeEntry("name", i18n("Mouse")); profile0.writeEntry("usePath", false); + profile0.writeEntry("useAssistants", false); + profile0.writeEntry("caps", 0.0); + profile0.writeEntry("timeInterval", 0); + profile0.writeEntry("distanceInterval", 0); + QString curveConfigMouse("\n\ntrue\n " + "true\n " + "\n\n]]>\n" + "false\n" + "true\n" + "1\n" + "" + "\n\n" + "0,0;1,1;\n\n]]>" + "false\n" + "true\n" + "1\n"); + profile0.writeEntry("curveConfig", curveConfigMouse); KConfigGroup profile1(&config, "Profile1"); - profile1.writeEntry("name", i18n("Graphics Pen")); + profile1.writeEntry("name", i18n("Brush")); profile1.writeEntry("usePath", false); - - generalGroup.writeEntry("profile", i18n("Mouse")); + profile1.writeEntry("useAssistants", false); + profile1.writeEntry("caps", 0.0); + profile1.writeEntry("timeInterval", 50); + profile1.writeEntry("distanceInterval", 15); + QString curveConfigBrush("\n\ntrue\n " + "true\n " + "\n\n]]>\n" + "true\n" + "true\n" + "1\n" + "" + "\n\n" + "0,0;0.375,0.25;0.625,0.75;1,1;\n\n]]>" + "true\n" + "true\n" + "1\n"); + profile1.writeEntry("curveConfig", curveConfigBrush); + + KConfigGroup profile2(&config, "Profile2"); + profile2.writeEntry("name", i18n("GPen")); + profile2.writeEntry("usePath", false); + profile2.writeEntry("useAssistants", false); + profile2.writeEntry("caps", 0.0); + profile2.writeEntry("timeInterval", 50); + profile2.writeEntry("distanceInterval", 15); + QString curveConfigGpen("\n\ntrue\n " + "true\n " + "\n\n]]>\n" + "true\n" + "true\n" + "1\n" + "" + "\n\n" + "0,0;0.625,0.375;1,1;\n\n]]>" + "true\n" + "true\n" + "1\n"); + profile2.writeEntry("curveConfig", curveConfigGpen); + + generalGroup.writeEntry("profile", i18n("Brush")); generalGroup.writeEntry("defaultProfilesAdded", true); config.sync(); } void KarbonCalligraphyOptionWidget::loadProfiles() { KConfig config(RCFILENAME); // load profiles as long as they are present int i = 0; while (1) { // forever KConfigGroup profileGroup(&config, "Profile" + QString::number(i)); // invalid profile, assume we reached the last one if (!profileGroup.hasKey("name")) { break; } Profile *profile = new Profile; profile->index = i; profile->name = profileGroup.readEntry("name", QString()); profile->usePath = profileGroup.readEntry("usePath", false); profile->useAssistants = profileGroup.readEntry("useAssistants", false); profile->caps = profileGroup.readEntry("caps", 0.0); profile->timeInterval = profileGroup.readEntry("timeInterval", 0.0); profile->distanceInterval = profileGroup.readEntry("distanceInterval", 0.0); + profile->curveConfig = new KisPropertiesConfiguration(); + profile->curveConfig->fromXML(profileGroup.readEntry("curveConfig", QString())); m_profiles.insert(profile->name, profile); ++i; } m_changingProfile = true; ProfileMap::const_iterator it = m_profiles.constBegin(); ProfileMap::const_iterator lastIt = m_profiles.constEnd(); for (; it != lastIt; ++it) { m_options->cmbProfiles->addItem(it.key()); } m_changingProfile = false; loadCurrentProfile(); } void KarbonCalligraphyOptionWidget::loadCurrentProfile() { KConfig config(RCFILENAME); KConfigGroup generalGroup(&config, "General"); QString currentProfile = generalGroup.readEntry("profile", QString()); // find the index needed by the comboBox int index = profilePosition(currentProfile); if (currentProfile.isEmpty() || index < 0) { return; } m_options->cmbProfiles->setCurrentIndex(index); Profile *profile = m_profiles[currentProfile]; m_changingProfile = true; m_options->rdAdjustPath->setChecked(profile->usePath); m_options->rdAdjustAssistant->setChecked(profile->useAssistants); if (profile->useAssistants == false && profile->usePath==false) { m_options->rdNoAdjust->setChecked(true); } m_options->sldCaps->setValue(profile->caps); m_options->sldTimeInterval->setValue(profile->timeInterval); m_options->sldDistanceInterval->setValue(profile->distanceInterval); + m_sizeOption->readOptionSetting(profile->curveConfig); + m_rotationOption->readOptionSetting(profile->curveConfig); m_changingProfile = false; } void KarbonCalligraphyOptionWidget::saveProfile(const QString &name) { Profile *profile = new Profile; profile->name = name; profile->usePath = m_options->rdAdjustPath->isChecked(); profile->caps = m_options->sldCaps->value(); profile->useAssistants = m_options->rdAdjustAssistant->isChecked(); profile->timeInterval = m_options->sldTimeInterval->value(); profile->distanceInterval = m_options->sldDistanceInterval->value(); + profile->curveConfig = new KisPropertiesConfiguration(); + m_sizeOption->writeOptionSetting(profile->curveConfig); + m_rotationOption->writeOptionSetting(profile->curveConfig); if (m_profiles.contains(name)) { // there is already a profile with the same name, overwrite profile->index = m_profiles[name]->index; m_profiles.insert(name, profile); } else { // it is a new profile profile->index = m_profiles.count(); m_profiles.insert(name, profile); // add the profile to the combobox QString dbg; for (int i = 0; i < m_options->cmbProfiles->count(); ++i) { dbg += m_options->cmbProfiles->itemText(i) + ' '; } int pos = profilePosition(name); m_changingProfile = true; m_options->cmbProfiles->insertItem(pos, name); m_changingProfile = false; for (int i = 0; i < m_options->cmbProfiles->count(); ++i) { dbg += m_options->cmbProfiles->itemText(i) + ' '; } } KConfig config(RCFILENAME); QString str = "Profile" + QString::number(profile->index); KConfigGroup profileGroup(&config, str); profileGroup.writeEntry("name", name); profileGroup.writeEntry("usePath", profile->usePath); profileGroup.writeEntry("useAssistants", profile->useAssistants); profileGroup.writeEntry("caps", profile->caps); profileGroup.writeEntry("timeInterval", profile->timeInterval); profileGroup.writeEntry("distanceInterval", profile->distanceInterval); + profileGroup.writeEntry("curveConfig", profile->curveConfig->toXML()); KConfigGroup generalGroup(&config, "General"); generalGroup.writeEntry("profile", name); config.sync(); m_options->cmbProfiles->setCurrentIndex(profilePosition(name)); } void KarbonCalligraphyOptionWidget::removeProfile(const QString &name) { int index = profilePosition(name); if (index < 0) { return; // no such profile } // remove the file from the config file KConfig config(RCFILENAME); int deletedIndex = m_profiles[name]->index; QString deletedGroup = "Profile" + QString::number(deletedIndex); config.deleteGroup(deletedGroup); config.sync(); // and from profiles m_profiles.remove(name); m_options->cmbProfiles->removeItem(index); // now in the config file there is value ProfileN missing, // where N = configIndex, so put the last one there if (m_profiles.isEmpty()) { return; } int lastN = -1; Profile *profile = 0; // profile to be moved, will be the last one Q_FOREACH (Profile *p, m_profiles) { if (p->index > lastN) { lastN = p->index; profile = p; } } Q_ASSERT(profile != 0); // do nothing if the deleted group was the last one if (deletedIndex > lastN) { return; } QString lastGroup = "Profile" + QString::number(lastN); config.deleteGroup(lastGroup); KConfigGroup profileGroup(&config, deletedGroup); profileGroup.writeEntry("name", name); profileGroup.writeEntry("usePath", profile->usePath); profileGroup.writeEntry("useAssistants", profile->useAssistants); profileGroup.writeEntry("caps", profile->caps); profileGroup.writeEntry("timeInterval", profile->timeInterval); profileGroup.writeEntry("distanceInterval", profile->distanceInterval); config.sync(); profile->index = deletedIndex; } int KarbonCalligraphyOptionWidget::profilePosition(const QString &profileName) { int res = 0; ProfileMap::const_iterator it = m_profiles.constBegin(); ProfileMap::const_iterator lastIt = m_profiles.constEnd(); for (; it != lastIt; ++it) { if (it.key() == profileName) { return res; } ++res; } return -1; } diff --git a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.h b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.h index 8be2072b86..1ea88b5adb 100644 --- a/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.h +++ b/plugins/tools/karbonplugins/tools/CalligraphyTool/KarbonCalligraphyOptionWidget.h @@ -1,128 +1,129 @@ /* This file is part of the KDE project Copyright (C) 2008 Fela Winkelmolen 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 KARBONCALLIGRAPHYOPTIONWIDGET_H #define KARBONCALLIGRAPHYOPTIONWIDGET_H #include #include #include class KComboBox; class QCheckBox; class QSpinBox; class QDoubleSpinBox; class QToolButton; class KarbonCalligraphyToolOptions; class KisCurveOptionWidget; class KarbonCalligraphyOptionWidget : public QWidget { Q_OBJECT public: explicit KarbonCalligraphyOptionWidget(); ~KarbonCalligraphyOptionWidget(); // emits all signals with the appropriate values // called once the signals are connected inside KarbonCalligraphyTool // to make sure all parameters are uptodate void emitAll(); Q_SIGNALS: // all the following signals emit user friendly values, not the internal // values which are instead computed directly by KarbonCalligraphyTool void usePathChanged(bool); void useAssistantChanged(bool); void useNoAdjustChanged(bool); void settingsChanged(KisPropertiesConfigurationSP settings); void massChanged(double); void dragChanged(double); void smoothTimeChanged(double); void smoothDistanceChanged(double); public Q_SLOTS: // needed for the shortcuts //void increaseWidth(); //void decreaseWidth(); //void increaseAngle(); //void decreaseAngle(); private Q_SLOTS: void loadProfile(const QString &name); //void toggleUseAngle(bool checked); void updateCurrentProfile(); void saveProfileAs(); void removeProfile(); void generateSettings(); private: // TODO: maybe make it a hash?? // is it needed al all?? struct Profile { QString name; int index; // index in the config file bool usePath; bool useAssistants; qreal caps; qreal timeInterval; qreal distanceInterval; + KisPropertiesConfigurationSP curveConfig; }; // convenience functions: // connects signals and slots void createConnections(); // if they aren't already added adds the default profiles // called by the ctor void addDefaultProfiles(); // laod the profiles from the configuration file void loadProfiles(); // loads the profile set as current profile in the configuration file void loadCurrentProfile(); // save a new profile using the values of the input boxes // if a profile with the same name already exists it will be overwritten void saveProfile(const QString &name); // removes the profile from the configuration file, from profiles // and from the combobox. // if the profile doesn't exist the function does nothing void removeProfile(const QString &name); // returns the position inside profiles of a certain profile // returns -1 if the profile is not found int profilePosition(const QString &profileName); private: typedef QMap ProfileMap; ProfileMap m_profiles; KarbonCalligraphyToolOptions *m_options; KisCurveOptionWidget *m_sizeOption; KisCurveOptionWidget *m_rotationOption; KisCurveOptionWidget *m_ratioOption; // when true updateCurrentProfile() doesn't do anything bool m_changingProfile; }; #endif // KARBONCALLIGRAPHYOPTIONWIDGET_H