File indexing completed on 2024-04-28 09:38:31

0001 /***************************************************************************
0002  *   Copyright (C) 2005,2006 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "dpline.h"
0012 #include "libraryitem.h"
0013 #include "resizeoverlay.h"
0014 #include "variant.h"
0015 
0016 #include <KLocalizedString>
0017 #include <QPainter>
0018 #include <cmath>
0019 #include <cstdlib>
0020 
0021 // BEGIN class DPLine
0022 Item *DPLine::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0023 {
0024     return new DPLine(itemDocument, newItem, id);
0025 }
0026 
0027 LibraryItem *DPLine::libraryItem()
0028 {
0029     return new LibraryItem(QStringList(QString("dp/line")), i18n("Line"), i18n("Other"), QIcon::fromTheme("text"), LibraryItem::lit_drawpart, DPLine::construct);
0030 }
0031 
0032 DPLine::DPLine(ItemDocument *itemDocument, bool newItem, const char *id)
0033     : DrawPart(itemDocument, newItem, id ? id : "line")
0034 {
0035     m_pLineOverlay = new LineOverlay(this);
0036     m_name = i18n("Line");
0037 
0038     createProperty("line-color", Variant::Type::Color);
0039     property("line-color")->setCaption(i18n("Line Color"));
0040     property("line-color")->setValue(QColor(Qt::black));
0041 
0042     createProperty("line-width", Variant::Type::Int);
0043     property("line-width")->setCaption(i18n("Line Width"));
0044     property("line-width")->setMinValue(1);
0045     property("line-width")->setMaxValue(1000);
0046     property("line-width")->setValue(1);
0047 
0048     createProperty("line-style", Variant::Type::PenStyle);
0049     property("line-style")->setCaption(i18n("Line Style"));
0050     property("line-style")->setAdvanced(true);
0051     setDataPenStyle("line-style", Qt::SolidLine);
0052 
0053     createProperty("cap-style", Variant::Type::PenCapStyle);
0054     property("cap-style")->setCaption(i18n("Cap Style"));
0055     property("cap-style")->setAdvanced(true);
0056     setDataPenCapStyle("cap-style", Qt::FlatCap);
0057 }
0058 
0059 DPLine::~DPLine()
0060 {
0061 }
0062 
0063 void DPLine::setSelected(bool yes)
0064 {
0065     if (yes == isSelected())
0066         return;
0067 
0068     DrawPart::setSelected(yes);
0069     m_pLineOverlay->showResizeHandles(yes);
0070 }
0071 
0072 void DPLine::dataChanged()
0073 {
0074     setPen(QPen(dataColor("line-color"), unsigned(dataInt("line-width")), getDataPenStyle("line-style"), getDataPenCapStyle("cap-style"), Qt::MiterJoin));
0075 
0076     postResize(); // in case the pen width has changed
0077     update();
0078 }
0079 
0080 void DPLine::postResize()
0081 {
0082     int x1 = offsetX();
0083     int y1 = offsetY();
0084     int x2 = x1 + width();
0085     int y2 = y1 + height();
0086 
0087     QPolygon p(4);
0088     int pw = pen().width();
0089     int dx = abs(x1 - x2);
0090     int dy = abs(y1 - y2);
0091     pw = pw * 4 / 3 + 2; // approx pw*sqrt(2)
0092     int px = x1 < x2 ? -pw : pw;
0093     int py = y1 < y2 ? -pw : pw;
0094     if (dx && dy && (dx > dy ? (dx * 2 / dy <= 2) : (dy * 2 / dx <= 2))) {
0095         // steep
0096         if (px == py) {
0097             p[0] = QPoint(x1, y1 + py);
0098             p[1] = QPoint(x2 - px, y2);
0099             p[2] = QPoint(x2, y2 - py);
0100             p[3] = QPoint(x1 + px, y1);
0101         } else {
0102             p[0] = QPoint(x1 + px, y1);
0103             p[1] = QPoint(x2, y2 - py);
0104             p[2] = QPoint(x2 - px, y2);
0105             p[3] = QPoint(x1, y1 + py);
0106         }
0107     } else if (dx > dy) {
0108         // horizontal
0109         p[0] = QPoint(x1 + px, y1 + py);
0110         p[1] = QPoint(x2 - px, y2 + py);
0111         p[2] = QPoint(x2 - px, y2 - py);
0112         p[3] = QPoint(x1 + px, y1 - py);
0113     } else {
0114         // vertical
0115         p[0] = QPoint(x1 + px, y1 + py);
0116         p[1] = QPoint(x2 + px, y2 - py);
0117         p[2] = QPoint(x2 - px, y2 - py);
0118         p[3] = QPoint(x1 - px, y1 + py);
0119     }
0120     setItemPoints(p, false);
0121 }
0122 
0123 void DPLine::drawShape(QPainter &p)
0124 {
0125     int x1 = int(x() + offsetX());
0126     int y1 = int(y() + offsetY());
0127     int x2 = x1 + width();
0128     int y2 = y1 + height();
0129 
0130     p.drawLine(x1, y1, x2, y2);
0131 }
0132 // END class DPLine
0133 
0134 // BEGIN class DPArrow
0135 Item *DPArrow::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0136 {
0137     return new DPArrow(itemDocument, newItem, id);
0138 }
0139 
0140 LibraryItem *DPArrow::libraryItem()
0141 {
0142     return new LibraryItem(QStringList(QString("dp/arrow")), i18n("Arrow"), i18n("Other"), QIcon::fromTheme("text"), LibraryItem::lit_drawpart, DPArrow::construct);
0143 }
0144 
0145 DPArrow::DPArrow(ItemDocument *itemDocument, bool newItem, const char *id)
0146     : DPLine(itemDocument, newItem, id ? id : "arrow")
0147 {
0148     m_name = i18n("Arrow");
0149 
0150     // We don't want to use the square cap style as it screws up drawing our arrow head
0151     QStringList allowed = property("cap-style")->allowed();
0152     allowed.removeAll(DrawPart::penCapStyleToName(Qt::SquareCap));
0153     property("cap-style")->setAllowed(allowed);
0154 
0155     m_headAngle = 20.0;
0156     Variant *v = createProperty("HeadAngle", Variant::Type::Double);
0157     v->setAdvanced(true);
0158     v->setCaption(i18n("Head angle"));
0159     v->setMinValue(10.0);
0160     v->setMaxValue(60.0);
0161     v->setUnit(QChar(0xb0));
0162     v->setValue(m_headAngle);
0163 }
0164 
0165 DPArrow::~DPArrow()
0166 {
0167 }
0168 
0169 void DPArrow::dataChanged()
0170 {
0171     DPLine::dataChanged();
0172     m_headAngle = dataDouble("HeadAngle");
0173     setChanged();
0174 }
0175 
0176 inline int round_x(double x)
0177 {
0178     return int(x + ((x > 0) ? 0.5 : -0.5));
0179 }
0180 
0181 void DPArrow::drawShape(QPainter &p)
0182 {
0183     double x1 = x() + offsetX();
0184     double y1 = y() + offsetY();
0185     double x2 = x1 + width();
0186     double y2 = y1 + height();
0187 
0188     p.drawLine(int(x1), int(y1), int(x2), int(y2));
0189 
0190     double dx = x2 - x1;
0191     double dy = y2 - y1;
0192 
0193     if (dx == 0. && dy == 0.)
0194         return;
0195 
0196     double arrow_angle = (dx == 0 ? (dy > 0 ? (M_PI_2) : (-M_PI_2)) : std::atan(dy / dx));
0197     if (dx < 0)
0198         arrow_angle += M_PI;
0199 
0200     double head_angle = M_PI * m_headAngle / 180.0;
0201     double head_length = 10.0;
0202 
0203     // Position of arrowhead
0204     double x3 = x2 + head_length * std::cos(M_PI + arrow_angle - head_angle);
0205     double y3 = y2 + head_length * std::sin(M_PI + arrow_angle - head_angle);
0206     double x4 = x2 + head_length * std::cos(M_PI + arrow_angle + head_angle);
0207     double y4 = y2 + head_length * std::sin(M_PI + arrow_angle + head_angle);
0208 
0209     // Draw arrowhead
0210     QPen pen = p.pen();
0211     pen.setCapStyle(Qt::RoundCap);
0212     p.setPen(pen);
0213     p.setBrush(pen.color());
0214     QPolygon pa(3);
0215     pa[0] = QPoint(round_x(x2), round_x(y2));
0216     pa[1] = QPoint(round_x(x3), round_x(y3));
0217     pa[2] = QPoint(round_x(x4), round_x(y4));
0218     p.drawPolygon(pa);
0219     p.drawPolyline(pa);
0220 }
0221 // END class DPLine