1 /***************************************************************************
2 * Copyright (C) 2003-2005 by David Saxton *
3 * david@bluehaze.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10
11 #include "canvasitemparts.h"
12 #include "cells.h"
13 #include "cnitem.h"
14 #include "icndocument.h"
15
16 #include <QWheelEvent>
17 #include <QPainter>
18 #include <QDebug>
19
20
21 //BEGIN Class GuiPart
GuiPart(CNItem * parent,const QRect & r,KtlQCanvas * canvas)22 GuiPart::GuiPart( CNItem *parent, const QRect & r, KtlQCanvas * canvas )
23 : //QObject(parent),
24 KtlQCanvasRectangle( r, canvas ),
25 m_angleDegrees(0),
26 p_parent(parent),
27 b_pointsAdded(false),
28 m_originalRect(r)
29 {
30 connect( parent, SIGNAL(movedBy(double, double )), this, SLOT(slotMoveBy(double, double )) );
31 setZ( parent->z() + 0.5 );
32 }
33
34
~GuiPart()35 GuiPart::~GuiPart()
36 {
37 hide();
38 }
39
40
setAngleDegrees(int angleDegrees)41 void GuiPart::setAngleDegrees( int angleDegrees )
42 {
43 m_angleDegrees = angleDegrees;
44 posChanged();
45 if (canvas())
46 canvas()->setChanged( boundingRect() );
47 }
48
49
setGuiPartSize(int width,int height)50 void GuiPart::setGuiPartSize( int width, int height )
51 {
52 updateConnectorPoints(false);
53 setSize( width, height );
54 posChanged();
55 }
56
57
initPainter(QPainter & p)58 void GuiPart::initPainter( QPainter &p )
59 {
60 if ( (m_angleDegrees%180) == 0 )
61 return;
62
63 p.translate( int(x()+(width()/2)), int(y()+(height()/2)) );
64 p.rotate(m_angleDegrees);
65 p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) );
66 }
67
68
deinitPainter(QPainter & p)69 void GuiPart::deinitPainter( QPainter &p )
70 {
71 if ( (m_angleDegrees%180) == 0 )
72 return;
73
74 p.translate( int(x()+(width()/2)), int(y()+(height()/2)) );
75 p.rotate(-m_angleDegrees);
76 p.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) );
77 }
78
79
slotMoveBy(double dx,double dy)80 void GuiPart::slotMoveBy( double dx, double dy )
81 {
82 if ( dx==0 && dy==0 )
83 return;
84
85 moveBy( dx, dy );
86 posChanged();
87 }
88
89
updateConnectorPoints(bool add)90 void GuiPart::updateConnectorPoints( bool add )
91 {
92 ICNDocument *icnd = dynamic_cast<ICNDocument*>(p_parent->itemDocument());
93 if ( !icnd)
94 return;
95
96 Cells * cells = icnd->cells();
97 if (!cells)
98 return;
99
100 if ( !isVisible() )
101 add = false;
102
103 if ( add == b_pointsAdded )
104 return;
105
106 b_pointsAdded = add;
107
108 int mult = add ? 1 : -1;
109 int sx = roundDown( x(), 8 );
110 int sy = roundDown( y(), 8 );
111 int ex = roundDown( x()+width(), 8 );
112 int ey = roundDown( y()+height(), 8 );
113
114 for ( int x=sx; x<=ex; ++x )
115 {
116 for ( int y=sy; y<=ey; ++y )
117 {
118 if ( cells->haveCell( x, y ) )
119 cells->cell( x, y ).CIpenalty += mult*ICNDocument::hs_item/2;
120 }
121 }
122 }
123
124
drawRect()125 QRect GuiPart::drawRect()
126 {
127 QRect dr = rect();
128 if ( m_angleDegrees%180 != 0 )
129 {
130 QMatrix m;
131 m.translate( int(x()+(width()/2)), int(y()+(height()/2)) );
132
133 if ( (m_angleDegrees%180) != 0 )
134 m.rotate(-m_angleDegrees);
135
136 m.translate( -int(x()+(width()/2)), -int(y()+(height()/2)) );
137
138 dr = m.mapRect(dr);
139 }
140 return dr;
141 }
142 //END Class GuiPart
143
144
145
146 //BEGIN Class Text
Text(const QString & text,CNItem * parent,const QRect & r,KtlQCanvas * canvas,int flags)147 Text::Text( const QString &text, CNItem *parent, const QRect & r, KtlQCanvas * canvas, int flags )
148 : GuiPart( parent, r, canvas )
149 {
150 m_flags = flags;
151 setText(text);
152 }
153
154
~Text()155 Text::~Text()
156 {
157 }
158
159
setText(const QString & text)160 bool Text::setText( const QString & text )
161 {
162 if ( m_text == text )
163 return false;
164
165 updateConnectorPoints(false);
166
167 m_text = text;
168 return true;
169 }
170
171
setFlags(int flags)172 void Text::setFlags( int flags )
173 {
174 updateConnectorPoints( false );
175 m_flags = flags;
176 }
177
178
drawShape(QPainter & p)179 void Text::drawShape( QPainter & p )
180 {
181 initPainter(p);
182 p.setFont( p_parent->font() );
183 p.drawText( drawRect(), m_flags, m_text );
184 deinitPainter(p);
185 }
186
187
recommendedRect() const188 QRect Text::recommendedRect() const
189 {
190 return QFontMetrics( p_parent->font() ).boundingRect( m_originalRect.x(), m_originalRect.y(), m_originalRect.width(), m_originalRect.height(), m_flags, m_text );
191 }
192 //END Class Text
193
194
195
196 //BEGIN Class Widget
Widget(const QString & id,CNItem * parent,const QRect & r,KtlQCanvas * canvas)197 Widget::Widget( const QString & id, CNItem * parent, const QRect & r, KtlQCanvas * canvas )
198 : GuiPart( parent, r, canvas )
199 {
200 m_id = id;
201 show();
202 }
203
~Widget()204 Widget::~Widget()
205 {
206 }
207
208
setEnabled(bool enabled)209 void Widget::setEnabled( bool enabled )
210 {
211 widget()->setEnabled(enabled);
212 }
213
214
posChanged()215 void Widget::posChanged()
216 {
217 // Swap around the width / height if we are rotated at a non-half way around
218 if ( m_angleDegrees%90 != 0 )
219 widget()->setFixedSize( QSize( height(), width() ) );
220 else
221 widget()->setFixedSize( size() );
222
223 widget()->move( int(x()), int(y()) );
224 }
225
226
drawShape(QPainter & p)227 void Widget::drawShape( QPainter &p )
228 {
229 // initPainter(p);
230 //p.drawPixmap( int(x()), int(y()), QPixmap::grabWidget( widget() ) ); // 2019.05.06
231 p.drawPixmap( int(x()), int(y()), widget()->grab() );
232 // deinitPainter(p);
233 }
234 //END Class Widget
235
236
237 //BEGIN Class ToolButton
ToolButton(QWidget * parent)238 ToolButton::ToolButton( QWidget *parent )
239 : QToolButton(parent)
240 {
241 m_angleDegrees = 0;
242 if ( QFontInfo(m_font).pixelSize() > 11 ) // It has to be > 11, not > 12, as (I think) pixelSize() rounds off the actual size
243 m_font.setPixelSize(12);
244 }
245
246
drawButtonLabel(QPainter * p)247 void ToolButton::drawButtonLabel( QPainter * p )
248 {
249 if ( m_angleDegrees % 180 == 0 || text().isEmpty() )
250 {
251 //QToolButton::drawButtonLabel(p);
252 QToolButton::render(p);
253 return;
254 }
255
256 double dx = size().width()/2;
257 double dy = size().height()/2;
258
259 p->translate( dx, dy );
260 p->rotate( m_angleDegrees );
261 p->translate( -dx, -dy );
262
263 p->translate( -dy+dx, 0 );
264
265 int m = width() > height() ? width() : height();
266
267 p->setPen( Qt::black );
268 p->drawText( isDown()?1:0, isDown()?1:0, m, m, Qt::AlignVCenter | Qt::AlignHCenter, text() );
269
270 p->translate( dy-dx, 0 );
271
272 p->translate( dx, dy );
273 p->rotate( -m_angleDegrees );
274 p->translate( -dx, -dy );
275 }
276 //END Class ToolButton
277
278
279 //BEGIN Class Button
Button(const QString & id,CNItem * parent,bool isToggle,const QRect & r,KtlQCanvas * canvas)280 Button::Button( const QString & id, CNItem * parent, bool isToggle, const QRect & r, KtlQCanvas * canvas )
281 : Widget( id, parent, r, canvas )
282 {
283 b_isToggle = isToggle;
284 m_button = new ToolButton(nullptr);
285 m_button->setToolButtonStyle(Qt::ToolButtonIconOnly);
286 m_button->setCheckable(b_isToggle);
287 connect( m_button, SIGNAL(pressed()), this, SLOT(slotStateChanged()) );
288 connect( m_button, SIGNAL(released()), this, SLOT(slotStateChanged()) );
289 posChanged();
290 }
291
292
~Button()293 Button::~Button()
294 {
295 delete m_button;
296 }
297
298
setToggle(bool toggle)299 void Button::setToggle( bool toggle )
300 {
301 if ( b_isToggle == toggle )
302 return;
303
304 if (b_isToggle)
305 {
306 // We must first untoggle it, else it'll be forever stuck...
307 setState(false);
308 }
309
310 b_isToggle = toggle;
311 m_button->setCheckable(b_isToggle);
312 }
313
314
posChanged()315 void Button::posChanged()
316 {
317 Widget::posChanged();
318 m_button->setAngleDegrees(m_angleDegrees);
319 }
320
slotStateChanged()321 void Button::slotStateChanged()
322 {
323 parent()->buttonStateChanged( id(), m_button->isDown() || m_button->isChecked() );
324 }
widget() const325 QWidget* Button::widget() const
326 {
327 return m_button;
328 }
setIcon(const QIcon & icon)329 void Button::setIcon( const QIcon &icon )
330 {
331 m_button->setIcon(icon);
332 }
setState(bool state)333 void Button::setState( bool state )
334 {
335 if ( this->state() == state )
336 return;
337
338 if ( isToggle() )
339 m_button->setChecked(state);
340 else
341 m_button->setDown(state);
342
343 slotStateChanged();
344 }
state() const345 bool Button::state() const
346 {
347 if ( isToggle() )
348 return m_button->isChecked(); //was: state()
349 else
350 return m_button->isDown();
351 }
352
353
recommendedRect() const354 QRect Button::recommendedRect() const
355 {
356 QSize sizeHint = m_button->sizeHint();
357 if ( sizeHint.width() < m_originalRect.width() )
358 sizeHint.setWidth( m_originalRect.width() );
359
360 // Hmm...for now, lets just keep the recomended rect the same height as the original rect
361 sizeHint.setHeight( m_originalRect.height() );
362
363 int hdw = (sizeHint.width() - m_originalRect.width())/2;
364 int hdh = (sizeHint.height() - m_originalRect.height())/2;
365
366 return QRect( m_originalRect.x()-hdw, m_originalRect.y()-hdh, sizeHint.width(), sizeHint.height() );
367 }
368
369
setText(const QString & text)370 void Button::setText( const QString &text )
371 {
372 if ( m_button->text() == text )
373 return;
374
375 updateConnectorPoints(false);
376
377 m_button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
378 m_button->setText(text);
379 m_button->setToolTip(text);
380 canvas()->setChanged( rect() );
381 p_parent->updateAttachedPositioning();
382 }
383
384
mousePressEvent(QMouseEvent * e)385 void Button::mousePressEvent( QMouseEvent *e )
386 {
387 if ( !m_button->isEnabled() )
388 return;
389
390 QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(),
391 // e->state() // 2018.12.02
392 e->buttons(), e->modifiers()
393 );
394 m_button->mousePressEvent(&event);
395 if (event.isAccepted())
396 e->accept();
397 canvas()->setChanged( rect() );
398 }
399
400
mouseReleaseEvent(QMouseEvent * e)401 void Button::mouseReleaseEvent( QMouseEvent *e )
402 {
403 QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(),
404 //e->state()
405 e->buttons(), e->modifiers()
406 );
407 m_button->mouseReleaseEvent(&event);
408 if (event.isAccepted())
409 e->accept();
410 canvas()->setChanged( rect() );
411 }
412
413
enterEvent(QEvent *)414 void Button::enterEvent(QEvent *)
415 {
416 m_button->enterEvent(nullptr);
417 // m_button->setFocus();
418 // bool hasFocus = m_button->hasFocus();
419 // m_button->setAutoRaise(true);
420 // m_button->setChecked(true);
421 }
422
leaveEvent(QEvent *)423 void Button::leaveEvent(QEvent *)
424 {
425 m_button->leaveEvent(nullptr);
426 // m_button->clearFocus();
427 // bool hasFocus = m_button->hasFocus();
428 // m_button->setAutoRaise(false);
429 // m_button->setChecked(false);
430 }
431 //END Class Button
432
433
434 //BEGIN Class SliderWidget
SliderWidget(QWidget * parent)435 SliderWidget::SliderWidget( QWidget *parent )
436 : QSlider(parent)
437 {
438 //setWFlags(Qt::WNoAutoErase|Qt::WRepaintNoErase);
439 //setWindowFlags(/*Qt::WNoAutoErase | */ Qt::WRepaintNoErase);
440 }
441 //END Class SliderWidget
442
443
444 //BEGIN Class Slider
Slider(const QString & id,CNItem * parent,const QRect & r,KtlQCanvas * canvas)445 Slider::Slider( const QString & id, CNItem * parent, const QRect & r, KtlQCanvas * canvas )
446 : Widget( id, parent, r, canvas )
447 {
448 m_orientation = Qt::Vertical;
449 m_bSliderInverted = false;
450
451 m_slider = new SliderWidget(nullptr);
452 QPalette p;
453 p.setColor(m_slider->foregroundRole(), Qt::white);
454 p.setColor(m_slider->backgroundRole(), Qt::transparent);
455 //m_slider->setPaletteBackgroundColor(Qt::white); // 2018.12.02
456 //m_slider->setPaletteForegroundColor(Qt::white);
457 //m_slider->setEraseColor(Qt::white);
458 //m_slider->setBackgroundMode( Qt::NoBackground );
459 m_slider->setPalette(p);
460 connect( m_slider, SIGNAL(valueChanged(int)), this, SLOT(slotValueChanged(int)) );
461 posChanged();
462 }
463
464
~Slider()465 Slider::~Slider()
466 {
467 delete m_slider;
468 }
469
470
widget() const471 QWidget* Slider::widget() const
472 {
473 return m_slider;
474 }
475
476
value() const477 int Slider::value() const
478 {
479 if ( m_bSliderInverted )
480 {
481 // Return the value as if the slider handle was reflected along through
482 // the center of the slide.
483 return m_slider->maximum() + m_slider->minimum() - m_slider->value();
484 }
485 else
486 return m_slider->value();
487 }
488
setValue(int value)489 void Slider::setValue( int value )
490 {
491 if ( m_bSliderInverted )
492 {
493 value = m_slider->maximum() + m_slider->minimum() - value;
494 }
495
496 m_slider->setValue( value );
497
498 if ( canvas() )
499 canvas()->setChanged( rect() );
500 }
501
502
mousePressEvent(QMouseEvent * e)503 void Slider::mousePressEvent( QMouseEvent *e )
504 {
505 qDebug() << Q_FUNC_INFO << "pos " << e->pos() << " x " << int(x()) << " y " << int(y())
506 << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers() ;
507 QMouseEvent event( QEvent::MouseButtonPress, e->pos()-QPoint(int(x()),int(y())), e->button(),
508 e->buttons(), e->modifiers() //e->state() // 2018.12.02
509 );
510 m_slider->mousePressEvent(&event);
511 if (event.isAccepted()) {
512 qDebug() << Q_FUNC_INFO << "accepted " << e;
513 e->accept();
514 }
515 canvas()->setChanged( rect() );
516 }
517
mouseReleaseEvent(QMouseEvent * e)518 void Slider::mouseReleaseEvent( QMouseEvent *e )
519 {
520 qDebug() << Q_FUNC_INFO << "pos " << e->pos() << " x " << int(x()) << " y " << int(y())
521 << " b " << e->button() << " bs " << e->buttons() << " m " << e->modifiers() ;
522 QMouseEvent event( QEvent::MouseButtonRelease, e->pos()-QPoint(int(x()),int(y())), e->button(),
523 e->buttons(), e->modifiers() //e->state() // 2018.12.02
524 );
525 m_slider->mouseReleaseEvent(&event);
526 if (event.isAccepted()) {
527 qDebug() << Q_FUNC_INFO << "accepted " << e;
528 e->accept();
529 }
530 canvas()->setChanged( rect() );
531 }
532
mouseDoubleClickEvent(QMouseEvent * e)533 void Slider::mouseDoubleClickEvent ( QMouseEvent *e )
534 {
535 QMouseEvent event( QEvent::MouseButtonDblClick, e->pos()-QPoint(int(x()),int(y())), e->button(),
536 e->buttons(), e->modifiers() //e->state() // 2018.12.02
537 );
538 m_slider->mouseDoubleClickEvent(&event);
539 if (event.isAccepted())
540 e->accept();
541 canvas()->setChanged( rect() );
542 }
543
mouseMoveEvent(QMouseEvent * e)544 void Slider::mouseMoveEvent( QMouseEvent *e )
545 {
546 QMouseEvent event( QEvent::MouseMove, e->pos()-QPoint(int(x()),int(y())), e->button(),
547 e->buttons(), e->modifiers() //e->state() //2018.12.02
548 );
549 m_slider->mouseMoveEvent(&event);
550 if (event.isAccepted())
551 e->accept();
552 }
553
wheelEvent(QWheelEvent * e)554 void Slider::wheelEvent( QWheelEvent *e )
555 {
556 QWheelEvent event( e->pos()-QPoint(int(x()),int(y())), e->delta(),
557 e->buttons(), e->modifiers(), // e->state(),
558 e->orientation() );
559 m_slider->wheelEvent(&event);
560 if (event.isAccepted())
561 e->accept();
562 canvas()->setChanged( rect() );
563 }
564
enterEvent(QEvent * e)565 void Slider::enterEvent(QEvent *e)
566 {
567 qDebug() << Q_FUNC_INFO;
568 m_slider->enterEvent(e);
569 }
570
leaveEvent(QEvent * e)571 void Slider::leaveEvent(QEvent *e)
572 {
573 qDebug() << Q_FUNC_INFO;
574 m_slider->leaveEvent(e);
575 }
576
slotValueChanged(int value)577 void Slider::slotValueChanged( int value )
578 {
579 if ( parent()->itemDocument() )
580 parent()->itemDocument()->setModified(true);
581
582 // Note that we do not use value as we want to take into account rotation
583 (void)value;
584 parent()->sliderValueChanged( id(), this->value() );
585
586 if ( canvas() )
587 canvas()->setChanged( rect() );
588 }
589
setOrientation(Qt::Orientation o)590 void Slider::setOrientation( Qt::Orientation o )
591 {
592 m_orientation = o;
593 posChanged();
594 }
595
posChanged()596 void Slider::posChanged()
597 {
598 Widget::posChanged();
599
600 bool nowInverted;
601
602 if ( m_orientation == Qt::Vertical )
603 {
604 nowInverted = angleDegrees() == 90 || angleDegrees() == 180;
605 m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Vertical : Qt::Horizontal );
606 }
607
608 else
609 {
610 nowInverted = angleDegrees() == 0 || angleDegrees() == 90;
611 m_slider->setOrientation( (m_angleDegrees%180 == 0) ? Qt::Horizontal : Qt::Vertical );
612 }
613
614 if ( nowInverted != m_bSliderInverted )
615 {
616 int prevValue = value();
617 m_bSliderInverted = nowInverted;
618 setValue( prevValue );
619 }
620 }
621 //END Class Slider
622