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