File indexing completed on 2024-05-19 09:39:33

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 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 "canvasitemparts.h"
0012 #include "cells.h"
0013 #include "cnitem.h"
0014 #include "icndocument.h"
0015 
0016 #include <QPainter>
0017 #include <QWheelEvent>
0018 #include <QStyle>
0019 #include <QStyleOptionSlider>
0020 #include <QStyleOptionToolButton>
0021 
0022 #include <ktechlab_debug.h>
0023 
0024 // BEGIN Class GuiPart
0025 GuiPart::GuiPart(CNItem *parent, const QRect &r, KtlQCanvas *canvas)
0026     : // QObject(parent),
0027     KtlQCanvasRectangle(r, canvas)
0028     , m_angleDegrees(0)
0029     , p_parent(parent)
0030     , b_pointsAdded(false)
0031     , m_originalRect(r)
0032 {
0033     connect(parent, &CNItem::movedBy, this, &GuiPart::slotMoveBy);
0034     setZ(parent->z() + 0.5);
0035 }
0036 
0037 GuiPart::~GuiPart()
0038 {
0039     hide();
0040 }
0041 
0042 void GuiPart::setAngleDegrees(int angleDegrees)
0043 {
0044     m_angleDegrees = angleDegrees;
0045     posChanged();
0046     if (canvas())
0047         canvas()->setChanged(boundingRect());
0048 }
0049 
0050 void GuiPart::setGuiPartSize(int width, int height)
0051 {
0052     updateConnectorPoints(false);
0053     setSize(width, height);
0054     posChanged();
0055 }
0056 
0057 void GuiPart::initPainter(QPainter &p)
0058 {
0059     if ((m_angleDegrees % 180) == 0)
0060         return;
0061 
0062     p.translate(int(x() + (width() / 2)), int(y() + (height() / 2)));
0063     p.rotate(m_angleDegrees);
0064     p.translate(-int(x() + (width() / 2)), -int(y() + (height() / 2)));
0065 }
0066 
0067 void GuiPart::deinitPainter(QPainter &p)
0068 {
0069     if ((m_angleDegrees % 180) == 0)
0070         return;
0071 
0072     p.translate(int(x() + (width() / 2)), int(y() + (height() / 2)));
0073     p.rotate(-m_angleDegrees);
0074     p.translate(-int(x() + (width() / 2)), -int(y() + (height() / 2)));
0075 }
0076 
0077 void GuiPart::slotMoveBy(double dx, double dy)
0078 {
0079     if (dx == 0 && dy == 0)
0080         return;
0081 
0082     moveBy(dx, dy);
0083     posChanged();
0084 }
0085 
0086 void GuiPart::updateConnectorPoints(bool add)
0087 {
0088     ICNDocument *icnd = dynamic_cast<ICNDocument *>(p_parent->itemDocument());
0089     if (!icnd)
0090         return;
0091 
0092     Cells *cells = icnd->cells();
0093     if (!cells)
0094         return;
0095 
0096     if (!isVisible())
0097         add = false;
0098 
0099     if (add == b_pointsAdded)
0100         return;
0101 
0102     b_pointsAdded = add;
0103 
0104     int mult = add ? 1 : -1;
0105     int sx = roundDown(x(), 8);
0106     int sy = roundDown(y(), 8);
0107     int ex = roundDown(x() + width(), 8);
0108     int ey = roundDown(y() + height(), 8);
0109 
0110     for (int x = sx; x <= ex; ++x) {
0111         for (int y = sy; y <= ey; ++y) {
0112             if (cells->haveCell(x, y))
0113                 cells->cell(x, y).CIpenalty += mult * ICNDocument::hs_item / 2;
0114         }
0115     }
0116 }
0117 
0118 QRect GuiPart::drawRect()
0119 {
0120     QRect dr = rect();
0121     if (m_angleDegrees % 180 != 0) {
0122         QTransform m;
0123         m.translate(int(x() + (width() / 2)), int(y() + (height() / 2)));
0124 
0125         if ((m_angleDegrees % 180) != 0)
0126             m.rotate(-m_angleDegrees);
0127 
0128         m.translate(-int(x() + (width() / 2)), -int(y() + (height() / 2)));
0129 
0130         dr = m.mapRect(dr);
0131     }
0132     return dr;
0133 }
0134 // END Class GuiPart
0135 
0136 // BEGIN Class Text
0137 Text::Text(const QString &text, CNItem *parent, const QRect &r, KtlQCanvas *canvas, int flags)
0138     : GuiPart(parent, r, canvas)
0139 {
0140     m_flags = flags;
0141     setText(text);
0142 }
0143 
0144 Text::~Text()
0145 {
0146 }
0147 
0148 bool Text::setText(const QString &text)
0149 {
0150     if (m_text == text)
0151         return false;
0152 
0153     updateConnectorPoints(false);
0154 
0155     m_text = text;
0156     return true;
0157 }
0158 
0159 void Text::setFlags(int flags)
0160 {
0161     updateConnectorPoints(false);
0162     m_flags = flags;
0163 }
0164 
0165 void Text::drawShape(QPainter &p)
0166 {
0167     initPainter(p);
0168     p.setFont(p_parent->font());
0169     p.drawText(drawRect(), m_flags, m_text);
0170     deinitPainter(p);
0171 }
0172 
0173 QRect Text::recommendedRect() const
0174 {
0175     return QFontMetrics(p_parent->font()).boundingRect(m_originalRect.x(), m_originalRect.y(), m_originalRect.width(), m_originalRect.height(), m_flags, m_text);
0176 }
0177 // END Class Text
0178 
0179 // BEGIN Class Widget
0180 Widget::Widget(const QString &id, CNItem *parent, const QRect &r, KtlQCanvas *canvas)
0181     : GuiPart(parent, r, canvas)
0182 {
0183     m_id = id;
0184     show();
0185 }
0186 
0187 Widget::~Widget()
0188 {
0189 }
0190 
0191 void Widget::setEnabled(bool enabled)
0192 {
0193     widget()->setEnabled(enabled);
0194 }
0195 
0196 void Widget::posChanged()
0197 {
0198     // Swap around the width / height if we are rotated at a non-half way around
0199     if (m_angleDegrees % 90 != 0)
0200         widget()->setFixedSize(QSize(height(), width()));
0201     else
0202         widget()->setFixedSize(size());
0203 
0204     widget()->move(int(x()), int(y()));
0205 }
0206 
0207 void Widget::drawShape(QPainter &p)
0208 {
0209     widget()->render(&p, QPoint(x(), y()));
0210 }
0211 // END Class Widget
0212 
0213 // BEGIN Class ToolButton
0214 ToolButton::ToolButton(QWidget *parent)
0215     : QToolButton(parent)
0216 {
0217     m_angleDegrees = 0;
0218     if (QFontInfo(m_font).pixelSize() > 11) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size
0219         m_font.setPixelSize(12);
0220 }
0221 
0222 void ToolButton::drawButtonLabel(QPainter *p)
0223 {
0224     if (m_angleDegrees % 180 == 0 || text().isEmpty()) {
0225         // QToolButton::drawButtonLabel(p);
0226         QToolButton::render(p);
0227         return;
0228     }
0229 
0230     double dx = size().width() / 2;
0231     double dy = size().height() / 2;
0232 
0233     p->translate(dx, dy);
0234     p->rotate(m_angleDegrees);
0235     p->translate(-dx, -dy);
0236 
0237     p->translate(-dy + dx, 0);
0238 
0239     int m = width() > height() ? width() : height();
0240 
0241     p->setPen(Qt::black);
0242     p->drawText(isDown() ? 1 : 0, isDown() ? 1 : 0, m, m, Qt::AlignVCenter | Qt::AlignHCenter, text());
0243 
0244     p->translate(dy - dx, 0);
0245 
0246     p->translate(dx, dy);
0247     p->rotate(-m_angleDegrees);
0248     p->translate(-dx, -dy);
0249 }
0250 // END Class ToolButton
0251 
0252 // BEGIN Class Button
0253 Button::Button(const QString &id, CNItem *parent, bool isToggle, const QRect &r, KtlQCanvas *canvas)
0254     : Widget(id, parent, r, canvas)
0255 {
0256     b_isToggle = isToggle;
0257     m_button = new ToolButton(nullptr);
0258     m_button->setToolButtonStyle(Qt::ToolButtonIconOnly);
0259     m_button->setCheckable(b_isToggle);
0260     connect(m_button, &ToolButton::pressed, this, &Button::slotStateChanged);
0261     connect(m_button, &ToolButton::released, this, &Button::slotStateChanged);
0262     posChanged();
0263 }
0264 
0265 Button::~Button()
0266 {
0267     delete m_button;
0268 }
0269 
0270 void Button::drawShape(QPainter &p)
0271 {
0272     const QTransform transform = p.worldTransform();
0273     p.setTransform(QTransform());
0274 
0275     QStyleOptionToolButton opt;
0276     opt.initFrom(m_button);
0277     m_button->initStyleOpt(&opt);
0278 
0279     opt.rect.translate(x(), y());
0280     opt.rect = transform.mapRect(opt.rect);
0281 
0282     m_button->style()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, m_button);
0283 
0284     p.setTransform(transform);
0285 }
0286 
0287 void Button::setToggle(bool toggle)
0288 {
0289     if (b_isToggle == toggle)
0290         return;
0291 
0292     if (b_isToggle) {
0293         // We must first untoggle it, else it'll be forever stuck...
0294         setState(false);
0295     }
0296 
0297     b_isToggle = toggle;
0298     m_button->setCheckable(b_isToggle);
0299 }
0300 
0301 void Button::posChanged()
0302 {
0303     Widget::posChanged();
0304     m_button->setAngleDegrees(m_angleDegrees);
0305 }
0306 
0307 void Button::slotStateChanged()
0308 {
0309     parent()->buttonStateChanged(id(), m_button->isDown() || m_button->isChecked());
0310 }
0311 QWidget *Button::widget() const
0312 {
0313     return m_button;
0314 }
0315 void Button::setIcon(const QIcon &icon)
0316 {
0317     m_button->setIcon(icon);
0318 }
0319 void Button::setState(bool state)
0320 {
0321     if (this->state() == state)
0322         return;
0323 
0324     if (isToggle())
0325         m_button->setChecked(state);
0326     else
0327         m_button->setDown(state);
0328 
0329     slotStateChanged();
0330 }
0331 bool Button::state() const
0332 {
0333     if (isToggle())
0334         return m_button->isChecked(); // was: state()
0335     else
0336         return m_button->isDown();
0337 }
0338 
0339 QRect Button::recommendedRect() const
0340 {
0341     QSize sizeHint = m_button->sizeHint();
0342     if (sizeHint.width() < m_originalRect.width())
0343         sizeHint.setWidth(m_originalRect.width());
0344 
0345     // Hmm...for now, lets just keep the recomended rect the same height as the original rect
0346     sizeHint.setHeight(m_originalRect.height());
0347 
0348     int hdw = (sizeHint.width() - m_originalRect.width()) / 2;
0349     int hdh = (sizeHint.height() - m_originalRect.height()) / 2;
0350 
0351     return QRect(m_originalRect.x() - hdw, m_originalRect.y() - hdh, sizeHint.width(), sizeHint.height());
0352 }
0353 
0354 void Button::setText(const QString &text)
0355 {
0356     if (m_button->text() == text)
0357         return;
0358 
0359     updateConnectorPoints(false);
0360 
0361     m_button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
0362     m_button->setText(text);
0363     m_button->setToolTip(text);
0364     canvas()->setChanged(rect());
0365     p_parent->updateAttachedPositioning();
0366 }
0367 
0368 void Button::mousePressEvent(QMouseEvent *e)
0369 {
0370     if (!m_button->isEnabled())
0371         return;
0372 
0373     QMouseEvent event(QEvent::MouseButtonPress,
0374                       e->pos() - QPoint(int(x()), int(y())),
0375                       e->button(),
0376                       //  e->state() // 2018.12.02
0377                       e->buttons(),
0378                       e->modifiers());
0379     m_button->mousePressEvent(&event);
0380     if (event.isAccepted())
0381         e->accept();
0382     canvas()->setChanged(rect());
0383 }
0384 
0385 void Button::mouseReleaseEvent(QMouseEvent *e)
0386 {
0387     QMouseEvent event(QEvent::MouseButtonRelease,
0388                       e->pos() - QPoint(int(x()), int(y())),
0389                       e->button(),
0390                       // e->state()
0391                       e->buttons(),
0392                       e->modifiers());
0393     m_button->mouseReleaseEvent(&event);
0394     if (event.isAccepted())
0395         e->accept();
0396     canvas()->setChanged(rect());
0397 }
0398 
0399 void Button::enterEvent(QEvent *)
0400 {
0401     m_button->enterEvent(nullptr);
0402     //  m_button->setFocus();
0403     //  bool hasFocus = m_button->hasFocus();
0404     //  m_button->setAutoRaise(true);
0405     //  m_button->setChecked(true);
0406 }
0407 
0408 void Button::leaveEvent(QEvent *)
0409 {
0410     m_button->leaveEvent(nullptr);
0411     //  m_button->clearFocus();
0412     //  bool hasFocus = m_button->hasFocus();
0413     //  m_button->setAutoRaise(false);
0414     //  m_button->setChecked(false);
0415 }
0416 // END Class Button
0417 
0418 // BEGIN Class SliderWidget
0419 SliderWidget::SliderWidget(QWidget *parent)
0420     : QSlider(parent)
0421 {
0422     // setWFlags(Qt::WNoAutoErase|Qt::WRepaintNoErase);
0423     // setWindowFlags(/*Qt::WNoAutoErase | */ Qt::WRepaintNoErase);
0424 }
0425 // END Class SliderWidget
0426 
0427 // BEGIN Class Slider
0428 Slider::Slider(const QString &id, CNItem *parent, const QRect &r, KtlQCanvas *canvas)
0429     : Widget(id, parent, r, canvas)
0430 {
0431     m_orientation = Qt::Vertical;
0432     m_bSliderInverted = false;
0433 
0434     m_slider = new SliderWidget(nullptr);
0435     QPalette p;
0436     p.setColor(m_slider->backgroundRole(), Qt::white);
0437     m_slider->setPalette(p);
0438     connect(m_slider, &SliderWidget::valueChanged, this, &Slider::slotValueChanged);
0439     posChanged();
0440 }
0441 
0442 Slider::~Slider()
0443 {
0444     delete m_slider;
0445 }
0446 
0447 QWidget *Slider::widget() const
0448 {
0449     return m_slider;
0450 }
0451 
0452 int Slider::value() const
0453 {
0454     if (m_bSliderInverted) {
0455         // Return the value as if the slider handle was reflected along through
0456         // the center of the slide.
0457         return m_slider->maximum() + m_slider->minimum() - m_slider->value();
0458     } else
0459         return m_slider->value();
0460 }
0461 
0462 void Slider::setValue(int value)
0463 {
0464     if (m_bSliderInverted) {
0465         value = m_slider->maximum() + m_slider->minimum() - value;
0466     }
0467 
0468     m_slider->setValue(value);
0469 
0470     if (canvas())
0471         canvas()->setChanged(rect());
0472 }
0473 
0474 void Slider::mousePressEvent(QMouseEvent *e)
0475 {
0476     qCDebug(KTL_LOG) << "pos " << e->pos() << " x " << int(x()) << " y " << int(y()) << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers();
0477     QMouseEvent event(QEvent::MouseButtonPress, e->pos() - QPoint(int(x()), int(y())), e->button(), e->buttons(), e->modifiers() // e->state() // 2018.12.02
0478     );
0479     m_slider->mousePressEvent(&event);
0480     if (event.isAccepted()) {
0481         qCDebug(KTL_LOG) << "accepted " << e;
0482         e->accept();
0483     }
0484     canvas()->setChanged(rect());
0485 }
0486 
0487 void Slider::mouseReleaseEvent(QMouseEvent *e)
0488 {
0489     qCDebug(KTL_LOG) << "pos " << e->pos() << " x " << int(x()) << " y " << int(y()) << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers();
0490     QMouseEvent event(QEvent::MouseButtonRelease, e->pos() - QPoint(int(x()), int(y())), e->button(), e->buttons(), e->modifiers() // e->state() // 2018.12.02
0491     );
0492     m_slider->mouseReleaseEvent(&event);
0493     if (event.isAccepted()) {
0494         qCDebug(KTL_LOG) << "accepted " << e;
0495         e->accept();
0496     }
0497     canvas()->setChanged(rect());
0498 }
0499 
0500 void Slider::mouseDoubleClickEvent(QMouseEvent *e)
0501 {
0502     QMouseEvent event(QEvent::MouseButtonDblClick, e->pos() - QPoint(int(x()), int(y())), e->button(), e->buttons(), e->modifiers() // e->state() // 2018.12.02
0503     );
0504     m_slider->mouseDoubleClickEvent(&event);
0505     if (event.isAccepted())
0506         e->accept();
0507     canvas()->setChanged(rect());
0508 }
0509 
0510 void Slider::mouseMoveEvent(QMouseEvent *e)
0511 {
0512     QMouseEvent event(QEvent::MouseMove, e->pos() - QPoint(int(x()), int(y())), e->button(), e->buttons(), e->modifiers() // e->state() //2018.12.02
0513     );
0514     m_slider->mouseMoveEvent(&event);
0515     if (event.isAccepted())
0516         e->accept();
0517 }
0518 
0519 void Slider::wheelEvent(QWheelEvent *e)
0520 {
0521     QWheelEvent event(e->position() - QPoint(int(x()), int(y())),
0522                    e->globalPosition() - QPoint(int(x()), int(y())),
0523                    e->pixelDelta(),
0524                    e->angleDelta(),
0525                    e->buttons(),
0526                    e->modifiers(),
0527                    e->phase(),
0528                    e->inverted());
0529     m_slider->wheelEvent(&event);
0530     if (event.isAccepted())
0531         e->accept();
0532     canvas()->setChanged(rect());
0533 }
0534 
0535 void Slider::enterEvent(QEvent *e)
0536 {
0537     qCDebug(KTL_LOG);
0538     m_slider->enterEvent(e);
0539 }
0540 
0541 void Slider::leaveEvent(QEvent *e)
0542 {
0543     qCDebug(KTL_LOG);
0544     m_slider->leaveEvent(e);
0545 }
0546 
0547 void Slider::slotValueChanged(int value)
0548 {
0549     if (parent()->itemDocument())
0550         parent()->itemDocument()->setModified(true);
0551 
0552     // Note that we do not use value as we want to take into account rotation
0553     (void)value;
0554     parent()->sliderValueChanged(id(), this->value());
0555 
0556     if (canvas())
0557         canvas()->setChanged(rect());
0558 }
0559 
0560 void Slider::setOrientation(Qt::Orientation o)
0561 {
0562     m_orientation = o;
0563     posChanged();
0564 }
0565 
0566 void Slider::drawShape(QPainter &p)
0567 {
0568     QStyleOptionSlider opt;
0569     opt.initFrom(m_slider);
0570     m_slider->initStyleOpt(&opt);
0571     opt.subControls = QStyle::SC_All;
0572 
0573     opt.rect.translate(x(), y());
0574     opt.rect = p.worldTransform().mapRect(opt.rect);
0575 
0576     p.setWorldMatrixEnabled(false);
0577     m_slider->style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, m_slider);
0578     p.setWorldMatrixEnabled(true);
0579 
0580 }
0581 
0582 void Slider::posChanged()
0583 {
0584     Widget::posChanged();
0585 
0586     bool nowInverted;
0587 
0588     if (m_orientation == Qt::Vertical) {
0589         nowInverted = angleDegrees() == 90 || angleDegrees() == 180;
0590         m_slider->setOrientation((m_angleDegrees % 180 == 0) ? Qt::Vertical : Qt::Horizontal);
0591     }
0592 
0593     else {
0594         nowInverted = angleDegrees() == 0 || angleDegrees() == 90;
0595         m_slider->setOrientation((m_angleDegrees % 180 == 0) ? Qt::Horizontal : Qt::Vertical);
0596     }
0597 
0598     if (m_slider->orientation() == Qt::Vertical) {
0599         m_slider->setFixedWidth(m_slider->sizeHint().width());
0600         m_slider->setFixedHeight(height());
0601     } else {
0602         m_slider->setFixedWidth(width());
0603         m_slider->setFixedHeight(m_slider->sizeHint().height());
0604     }
0605 
0606     if (nowInverted != m_bSliderInverted) {
0607         int prevValue = value();
0608         m_bSliderInverted = nowInverted;
0609         setValue(prevValue);
0610     }
0611 }
0612 // END Class Slider
0613 
0614 #include "moc_canvasitemparts.cpp"