box_item.cpp 7.45 KB
Newer Older
ita1024's avatar
ita1024 committed
1
// Thomas Nagy 2007-2018 GPLV3
tnagy1024's avatar
tnagy1024 committed
2 3 4

#include <QApplication>
#include <QAbstractTextDocumentLayout>
5
#include <QInputDialog>
tnagy1024's avatar
tnagy1024 committed
6 7 8 9
#include <QTextDocument>
#include <QTextDocumentFragment>
#include <QAbstractTextDocumentLayout>
#include <QTextList>
tnagy1024's avatar
tnagy1024 committed
10
#include <QGraphicsSceneMouseEvent>
tnagy1024's avatar
tnagy1024 committed
11
#include <QLinearGradient>
tnagy1024's avatar
tnagy1024 committed
12 13 14
#include <QClipboard>
#include <QPainter>
#include <QtDebug>
15
#include <QAction>
tnagy1024's avatar
tnagy1024 committed
16 17 18
#include <QTextDocument>
#include "box_item.h"
#include "box_view.h"
19
 #include "box_link.h"
20
#include "data_item.h"
tnagy1024's avatar
tnagy1024 committed
21
#include "sem_mediator.h"
tnagy1024's avatar
tnagy1024 committed
22
#include "mem_box.h"
23
#include "special_edit.h"
tnagy1024's avatar
tnagy1024 committed
24

25 26
box_item::box_item(box_view* i_oParent, int i_iId) : QGraphicsRectItem(), connectable(), editable(), resizable(), m_oView(i_oParent),
m_oItem(m_oView->m_oMediator->m_oItems[m_oView->m_iId])
tnagy1024's avatar
tnagy1024 committed
27
{
28
	m_iId = i_iId;
tnagy1024's avatar
tnagy1024 committed
29

30
	m_oBox = m_oItem.m_oBoxes[m_iId];
tnagy1024's avatar
tnagy1024 committed
31
	Q_ASSERT(m_oBox);
tnagy1024's avatar
tnagy1024 committed
32

33
	i_oParent->scene()->addItem(this);
tnagy1024's avatar
tnagy1024 committed
34 35
	m_oChain = new box_chain(i_oParent);
	m_oChain->setParentItem(this);
tnagy1024's avatar
tnagy1024 committed
36

ita1024's avatar
ita1024 committed
37
	m_oResize = new box_resize_point(m_oView, this);
ita1024's avatar
ita1024 committed
38
	m_oResize->setRect(-CTRLSIZE, -CTRLSIZE, CTRLSIZE, CTRLSIZE);
ita1024's avatar
ita1024 committed
39 40 41 42
	m_oResize->setCursor(Qt::SizeFDiagCursor);
	m_oResize->hide();
	m_oResize->setParentItem(this);

tnagy1024's avatar
tnagy1024 committed
43
	update_size();
ita1024's avatar
ita1024 committed
44
	update_sizers();
45
	setZValue(100);
46
	//setCursor(Qt::SizeFDiagCursor);
tnagy1024's avatar
tnagy1024 committed
47
	setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
ita1024's avatar
ita1024 committed
48 49
	x_text_off = 2 * OFF;
	y_text_off = 2 * OFF;
tnagy1024's avatar
tnagy1024 committed
50 51
}

52
box_item::~box_item()
tnagy1024's avatar
tnagy1024 committed
53
{
tnagy1024's avatar
tnagy1024 committed
54
	delete m_oChain;
ita1024's avatar
ita1024 committed
55
	delete m_oResize;
tnagy1024's avatar
tnagy1024 committed
56 57
}

tnagy1024's avatar
tnagy1024 committed
58 59
void box_item::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Thomas Nagy's avatar
Thomas Nagy committed
60
	doc.setDefaultFont(scene()->font());
61 62 63 64
	QTextOption l_oOption = doc.defaultTextOption();
	l_oOption.setAlignment(m_oBox->m_iAlign);
	doc.setDefaultTextOption(l_oOption);

65 66 67 68 69
	QPen l_oPen = QPen(Qt::SolidLine);

	l_oPen.setColor(Qt::black);
	if (isSelected()) l_oPen.setStyle(Qt::DotLine);
	l_oPen.setCosmetic(false);
70
	l_oPen.setWidthF(0.01 + 1);
71

ita1024's avatar
ita1024 committed
72 73 74
	qreal pad = l_oPen.width() / 2.;
	QRectF l_oRect = rect().adjusted(pad, pad, -pad, -pad);

75
	painter->setPen(l_oPen);
tnagy1024's avatar
tnagy1024 committed
76

77
	QColor bc = m_oBox->getColor(m_oView->m_oMediator);
78 79 80 81 82 83 84 85 86 87 88 89
	if (m_oView->m_bDisableGradient)
	{
		painter->setBrush(bc);
	}
	else
	{
		QLinearGradient linearGradient(0, 0, l_oRect.width(), 0);
		linearGradient.setColorAt(0.0, bc);
		linearGradient.setColorAt(1.0, bc.darker(GRADVAL));
		painter->setBrush(linearGradient);
	}

90

tnagy1024's avatar
tnagy1024 committed
91
	painter->drawRoundRect(l_oRect, 20, 20);
92

tnagy1024's avatar
tnagy1024 committed
93 94
	QAbstractTextDocumentLayout::PaintContext ctx;
	ctx.palette = QApplication::palette("QTextControl");
tnagy1024's avatar
tnagy1024 committed
95
	ctx.palette.setColor(QPalette::Text, Qt::black); // white on black kde themes
tnagy1024's avatar
tnagy1024 committed
96 97 98
	QAbstractTextDocumentLayout * lay = doc.documentLayout();
	qreal yoff = lay->documentSize().height();

ita1024's avatar
ita1024 committed
99
	painter->translate(OFF, OFF + (rect().height() - 2 * OFF - yoff) / 2.);
tnagy1024's avatar
tnagy1024 committed
100
	lay->draw(painter, ctx);
tnagy1024's avatar
tnagy1024 committed
101 102
}

103
void box_item::update_data() {
tnagy1024's avatar
tnagy1024 committed
104
	setPos(QPointF(m_oBox->m_iXX, m_oBox->m_iYY));
105
	if (m_oBox->m_iWW != m_iWW || m_oBox->m_iHH != m_iHH || doc.toPlainText() != m_oBox->m_sText || doc.textWidth() != m_oBox->m_iWW - x_text_off)
tnagy1024's avatar
tnagy1024 committed
106
	{
tnagy1024's avatar
tnagy1024 committed
107
		update_size();
tnagy1024's avatar
tnagy1024 committed
108 109
	}
	update();
ita1024's avatar
ita1024 committed
110
	update_sizers();
111
}
tnagy1024's avatar
tnagy1024 committed
112

tnagy1024's avatar
tnagy1024 committed
113
void box_item::update_size() {
tnagy1024's avatar
tnagy1024 committed
114 115 116
	m_iWW = m_oBox->m_iWW;
	m_iHH = m_oBox->m_iHH;

117 118 119 120 121
	QTextOption l_oOption = doc.defaultTextOption();
	l_oOption.setAlignment(m_oBox->m_iAlign);
	doc.setDefaultTextOption(l_oOption);
	doc.setPlainText(m_oBox->m_sText);

ita1024's avatar
ita1024 committed
122
	doc.setTextWidth(m_iWW - x_text_off);
tnagy1024's avatar
tnagy1024 committed
123

124
	prepareGeometryChange();
tnagy1024's avatar
tnagy1024 committed
125
	setRect(0, 0, m_iWW, m_iHH);
tnagy1024's avatar
tnagy1024 committed
126
	m_oChain->setPos(m_iWW + 3, 0);
127
	update_links();
ita1024's avatar
ita1024 committed
128
	update_sizers();
tnagy1024's avatar
tnagy1024 committed
129 130
}

131 132
void box_item::properties()
{
133 134
	special_edit_properties l_oDlg(m_oView, this);
	l_oDlg.exec();
tnagy1024's avatar
tnagy1024 committed
135 136
}

137 138
QVariant box_item::itemChange(GraphicsItemChange i_oChange, const QVariant &i_oValue)
{
tnagy1024's avatar
tnagy1024 committed
139
	if (scene())
140
	{
tnagy1024's avatar
tnagy1024 committed
141 142 143 144 145 146 147 148 149 150
		if (i_oChange == ItemPositionChange)
		{
			QPointF np = i_oValue.toPointF();
			np.setX(((int) np.x() / GRID) * GRID);
			np.setY(((int) np.y() / GRID) * GRID);
			return np;
		}
		else if (i_oChange == ItemPositionHasChanged)
		{
			update_links();
ita1024's avatar
ita1024 committed
151
			update_sizers();
tnagy1024's avatar
tnagy1024 committed
152
		}
tnagy1024's avatar
tnagy1024 committed
153 154
		else if (i_oChange == ItemSelectedHasChanged)
		{
155
			update_selection();
tnagy1024's avatar
tnagy1024 committed
156
		}
157 158
	}

tnagy1024's avatar
tnagy1024 committed
159
	return QGraphicsItem::itemChange(i_oChange, i_oValue);
160 161
}

162 163 164 165 166 167
void box_item::update_selection()
{
	m_oChain->setVisible(isSelected());
	m_oResize->setVisible(isSelected());
}

168 169 170 171
void box_item::update_links()
{
	foreach (box_link* l_oLink, m_oView->m_oLinks)
	{
172 173 174 175
		if (l_oLink->m_oInnerLink.m_iParent == m_iId || l_oLink->m_oInnerLink.m_iChild == m_iId)
		{
			l_oLink->update_pos();
		}
176 177 178
	}
}

tnagy1024's avatar
tnagy1024 committed
179 180
static int RATIO[] = {333, 500, 667, 0};

181
int box_item::choose_position(const QPointF& i_oP, box_link*, box_control_point*)
tnagy1024's avatar
tnagy1024 committed
182
{
ita1024's avatar
ita1024 committed
183
	QRectF r = rectPos();
tnagy1024's avatar
tnagy1024 committed
184 185 186
	QPointF l_o = pos() - i_oP + QPointF(r.width()/2, r.height()/2);
	double c_x = l_o.x() * r.height();
	double c_y = l_o.y() * r.width();
tnagy1024's avatar
tnagy1024 committed
187 188 189 190

	int ret = 0;
	int best = 1<<30;
	int cand = 0;
tnagy1024's avatar
tnagy1024 committed
191 192
	if (qAbs(c_x) > qAbs(c_y))
	{
tnagy1024's avatar
tnagy1024 committed
193 194 195 196 197 198 199 200 201 202 203 204 205
		ret = (c_x > 0) ? data_link::WEST : data_link::EAST;
		for (int i=0; i < 10; ++i) {
			int k = RATIO[i];
			if (k == 0) break;
			int val = qAbs((k * r.height() / 1000.) - (i_oP.y() - pos().y()));

			if (val < best)
			{
				best = val;
				cand = k;
			}
		}
		ret += cand * MUL;
tnagy1024's avatar
tnagy1024 committed
206 207 208
	}
	else
	{
tnagy1024's avatar
tnagy1024 committed
209 210 211 212 213 214 215 216 217 218 219 220 221
		ret = (c_y > 0) ? data_link::NORTH : data_link::SOUTH;
		for (int i=0; i < 10; ++i) {
			int k = RATIO[i];
			if (k == 0) break;
			int val = qAbs((k * r.width() / 1000.) - (i_oP.x() - pos().x()));

			if (val < best)
			{
				best = val;
				cand = k;
			}
		}
		ret += cand * MUL;
tnagy1024's avatar
tnagy1024 committed
222
	}
tnagy1024's avatar
tnagy1024 committed
223
	return ret;
tnagy1024's avatar
tnagy1024 committed
224 225
}

tnagy1024's avatar
tnagy1024 committed
226 227
QPoint box_item::get_point(int i_oP)
{
ita1024's avatar
ita1024 committed
228
	QRectF r = rectPos();
tnagy1024's avatar
tnagy1024 committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
	int ratio = i_oP / MUL;

	if (ratio >= 1000 || ratio <= 0) ratio = 500;
	switch (i_oP & data_link::COORD) {
		case data_link::NORTH:
			return QPoint(r.x() + r.width() * ratio / 1000., r.y());
		case data_link::WEST:
			return QPoint(r.x(), r.y() + r.height() * ratio / 1000.);
		case data_link::SOUTH:
			return QPoint(r.x() + r.width() * ratio / 1000., r.y() + r.height());
		case data_link::EAST:
			return QPoint(r.x() + r.width(), r.y() + r.height() * ratio / 1000.);
	}
	Q_ASSERT(false);
	return QPoint(0, 0);
}
tnagy1024's avatar
tnagy1024 committed
245

ita1024's avatar
ita1024 committed
246 247
QSize box_item::best_size(const QPointF &dims)
{
ita1024's avatar
ita1024 committed
248 249 250 251 252
	if (doc.toPlainText().isEmpty())
	{
		return QSize(fceil(dims.x(), GRID), fceil(dims.y(), GRID));
	}

ita1024's avatar
ita1024 committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	int x = dims.x();
	x = GRID * (x / GRID);
	if (x < GRID) x = GRID;

	while (true) {
		int l_oWantedW = x - x_text_off;
		doc.setTextWidth(l_oWantedW);
		if (doc.size().width() <= l_oWantedW)
			break;
		x += GRID;
	}

	int y = dims.y();
	y = GRID * (y / GRID);
	if (y < GRID) y = GRID;
	while (y - y_text_off < doc.size().height())
	{
		y += GRID;
	}
	return QSize(x, y);
}

QPointF box_item::validate_point(box_resize_point *p, const QPointF & orig)
{
	QSize s = best_size(orig);
	m_iLastStretchX = s.width();
	m_iLastStretchY = s.height();
	m_oChain->setPos(m_iLastStretchX + 3, 0);

	prepareGeometryChange();
	setRect(0, 0, m_iLastStretchX, m_iLastStretchY);
	update();
	update_links();
286
	m_oView->message(i18n("%1 x %2", QString::number(m_iLastStretchX), QString::number(m_iLastStretchY)), 1000);
ita1024's avatar
ita1024 committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	return QPointF(m_iLastStretchX, m_iLastStretchY);
}

void box_item::commit_size(box_resize_point *p)
{
	QRect r_orig(m_oBox->m_iXX, m_oBox->m_iYY, m_oBox->m_iWW, m_oBox->m_iHH);
	QRect r_dest(m_oBox->m_iXX, m_oBox->m_iYY, m_iLastStretchX, m_iLastStretchY);

	if (r_orig != r_dest)
	{
		mem_size_box *mem = new mem_size_box(m_oView->m_oMediator, m_oView->m_iId);
		mem->prev_values[m_oBox] = r_orig;
		mem->next_values[m_oBox] = r_dest;
		mem->apply();
	}
}

void box_item::freeze(bool b)
{
	if (b)
	{
		setFlags(ItemIsSelectable);
		m_iLastStretchX = 0;
		m_iLastStretchY = 0;
	}
	else
	{
		setFlags(ItemIsMovable | ItemIsSelectable | ItemSendsGeometryChanges);
	}
}

void box_item::update_sizers()
{
	m_oResize->setPos(m_oBox->m_iWW, m_oBox->m_iHH);
}