1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 /***************************************************************************
8 						  pageitem.cpp  -  description
9 							 -------------------
10 	begin                : Sat Apr 7 2001
11 	copyright            : (C) 2001 by Franz Schmid
12 	email                : Franz.Schmid@altmuehlnet.de
13  ***************************************************************************/
14 
15 /***************************************************************************
16  *                                                                         *
17  *   This program is free software; you can redistribute it and/or modify  *
18  *   it under the terms of the GNU General Public License as published by  *
19  *   the Free Software Foundation; either version 2 of the License, or     *
20  *   (at your option) any later version.                                   *
21  *                                                                         *
22  ***************************************************************************/
23 
24 #include "pageitem_textframe.h"
25 
26 #include <QDebug>
27 #include <QList>
28 #include <QTransform>
29 #include <QPalette>
30 #include <QPoint>
31 #include <QPolygon>
32 #include <QRegion>
33 #include <cairo.h>
34 #include <cassert>
35 
36 #include "actionmanager.h"
37 #include "appmodes.h"
38 #include "canvas.h"
39 #include "commonstrings.h"
40 #include "hyphenator.h"
41 #include "marks.h"
42 #include "notesstyles.h"
43 #include "numeration.h"
44 #include "pageitem.h"
45 #include "pageitem_group.h"
46 #include "pageitem_noteframe.h"
47 #include "prefsmanager.h"
48 #include "scconfig.h"
49 #include "scpage.h"
50 #include "scpainter.h"
51 #include "scpaths.h"
52 #include "scraction.h"
53 #include "scribus.h"
54 #include "scribusdoc.h"
55 #include "scribusview.h"
56 #include "scribusstructs.h"
57 #include "selection.h"
58 #include "text/boxes.h"
59 #include "text/screenpainter.h"
60 #include "text/textshaper.h"
61 #include "text/shapedtext.h"
62 #include "text/shapedtextfeed.h"
63 #include "ui/guidemanager.h"
64 #include "ui/marksmanager.h"
65 #include "undomanager.h"
66 #include "undostate.h"
67 #include "units.h"
68 #include "util.h"
69 #include "util_math.h"
70 
71 
72 
73 using namespace std;
74 
PageItem_TextFrame(ScribusDoc * pa,double x,double y,double w,double h,double w2,const QString & fill,const QString & outline)75 PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline)
76 	: PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline)
77 {
78 	init();
79 	firstChar = 0;
80 }
81 
PageItem_TextFrame(const PageItem & p)82 PageItem_TextFrame::PageItem_TextFrame(const PageItem & p) : PageItem(p)
83 {
84 	init();
85 	m_notesFramesMap.clear();
86 }
87 
init()88 void PageItem_TextFrame::init()
89 {
90 	invalid = true;
91 	cursorBiasBackward = false;
92 	unicodeTextEditMode = false;
93 	unicodeInputCount = 0;
94 	m_origAnnotPos = QRectF(xPos(), yPos(), width(), height());
95 	verticalAlign = 0;
96 	incompleteLines = 0;
97 	maxY = 0.0;
98 	connect(&itemText,SIGNAL(changed(int,int)), this, SLOT(slotInvalidateLayout(int,int)));
99 }
100 
calcAvailableRegion()101 QRegion PageItem_TextFrame::calcAvailableRegion()
102 {
103 	QRegion result(this->Clip);
104 	if (isEmbedded && !isGroupChild())
105 		return result;
106 
107 	bool invertible(false);
108 	QTransform canvasToLocalMat;
109 	if (isGroupChild())
110 		canvasToLocalMat.translate(gXpos, gYpos);
111 	else
112 		canvasToLocalMat.translate(m_xPos, m_yPos);
113 	canvasToLocalMat.rotate(m_rotation);
114 	canvasToLocalMat = canvasToLocalMat.inverted(&invertible);
115 
116 	if (!invertible)
117 		return QRegion();
118 
119 	int layerLev = m_Doc->layerLevelFromID(m_layerID);
120 	int docItemsCount = m_Doc->Items->count();
121 	PageItem* docItem = nullptr;
122 	int layerLevItem;
123 	QList<PageItem*> thisList;
124 	if (!OnMasterPage.isEmpty())
125 	{
126 		if ((savedOwnPage == -1) || (savedOwnPage >= signed(m_Doc->Pages->count())))
127 			return result;
128 		ScPage* Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
129 		ScPage* Dp = m_Doc->Pages->at(savedOwnPage);
130 		if (isGroupChild())
131 			thisList = Parent->asGroupFrame()->groupItemList;
132 		else
133 			thisList = m_Doc->MasterItems;
134 		int thisid = thisList.indexOf(this);
135 		for (int i = 0; i < m_Doc->MasterItems.count(); ++i)
136 		{
137 			docItem = m_Doc->MasterItems.at(i);
138 			// #10642 : masterpage items interact only with items placed on same masterpage
139 			if (docItem->OnMasterPage != OnMasterPage)
140 				continue;
141 			layerLevItem = m_Doc->layerLevelFromID(docItem->m_layerID);
142 			if (((i > thisid) && (docItem->m_layerID == m_layerID)) || (layerLevItem > layerLev && m_Doc->layerFlow(docItem->m_layerID)))
143 			{
144 				if (docItem->textFlowAroundObject())
145 				{
146 					QRegion itemRgn = docItem->textInteractionRegion(Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset());
147 					result = result.subtracted( canvasToLocalMat.map(itemRgn) );
148 				}
149 			}
150 		} // for all masterItems
151 		// (JG) #6009 : disable possible interaction between master text frames and normal frames
152 		// which have the text flow option set
153 		/*if (!m_Doc->masterPageMode())
154 		{
155 			for (uint i = 0; i < docItemsCount; ++i)
156 			{
157 				docItem = m_Doc->Items->at(i);
158 				Mp = m_Doc->MasterPages.at(m_Doc->MasterNames[OnMasterPage]);
159 				Dp = m_Doc->Pages->at(OwnPage);
160 				if ((docItem->textFlowAroundObject()) && (docItem->OwnPage == OwnPage))
161 				{
162 					result = result.subtract(itemShape(docItem, m_Doc->view(), Mp->xOffset() - Dp->xOffset(), Mp->yOffset() - Dp->yOffset()));
163 				}
164 			} // for all docItems
165 		} // if (! masterPageMode) */
166 	} // if (!OnMasterPage.isEmpty())
167 	else
168 	{
169 		int thisid = 0;
170 		if (isGroupChild())
171 		{
172 			thisid = Parent->asGroupFrame()->groupItemList.indexOf(this);
173 			docItemsCount = Parent->asGroupFrame()->groupItemList.count();
174 			for (int i = thisid + 1; i < docItemsCount; ++i)
175 			{
176 				docItem = Parent->asGroupFrame()->groupItemList.at(i);
177 				if (docItem->textFlowAroundObject())
178 				{
179 					QRegion itemRgn = docItem->textInteractionRegion(0, 0);
180 					result = result.subtracted( canvasToLocalMat.map(itemRgn) );
181 				}
182 			}
183 		}
184 		else
185 		{
186 			thisid = m_Doc->Items->indexOf(this);
187 			for (int i = 0; i < docItemsCount; ++i)
188 			{
189 				docItem = m_Doc->Items->at(i);
190 				layerLevItem = m_Doc->layerLevelFromID(docItem->m_layerID);
191 				if (((i > thisid) && (docItem->m_layerID == m_layerID)) || (layerLevItem > layerLev && m_Doc->layerFlow(docItem->m_layerID)))
192 				{
193 					if (docItem->textFlowAroundObject())
194 					{
195 						QRegion itemRgn = docItem->textInteractionRegion(0, 0);
196 						result = result.subtracted( canvasToLocalMat.map(itemRgn) );
197 					}
198 				}
199 			}
200 		} // for all docItems
201 	} // if(OnMasterPage.isEmpty()
202 
203 	return result;
204 }
205 
setShadow()206 void PageItem_TextFrame::setShadow()
207 {
208 	if (OnMasterPage.isEmpty())
209 		return;
210 
211 	QString newShadow = m_Doc->masterPageMode() ? OnMasterPage : QString::number(OwnPage);
212 	if (newShadow == m_currentShadow)
213 		return;
214 
215 	if (m_currentShadow == OnMasterPage)
216 	{
217 		// masterpage was edited, clear all shadows
218 		m_shadows.clear();
219 	}
220 	if (!m_shadows.contains(newShadow))
221 	{
222 		if (!m_shadows.contains(OnMasterPage))
223 		{
224 			m_shadows[OnMasterPage] = itemText;
225 //			const ParagraphStyle& pstyle(shadows[OnMasterPage].paragraphStyle(0));
226 //			qDebug() << QString("Pageitem_Textframe: style of master: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
227 //			qDebug() << QString("Pageitem_Textframe: shadow itemText->%1").arg(OnMasterPage);
228 		}
229 		if (newShadow != OnMasterPage)
230 		{
231 			m_shadows[newShadow] = m_shadows[OnMasterPage].copy();
232 //			const ParagraphStyle& pstyle(shadows[newShadow].paragraphStyle(0));
233 //			qDebug() << QString("Pageitem_Textframe: style of shadow copy: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
234 		}
235 //		qDebug() << QString("Pageitem_Textframe: shadow %1<-%2").arg(newShadow).arg(OnMasterPage);
236 	}
237 	itemText = m_shadows[newShadow];
238 //	const ParagraphStyle& pstyle(itemText.paragraphStyle(0));
239 //	qDebug() << QString("Pageitem_Textframe: style of shadow: %1 align=%2").arg(pstyle.parent()).arg(pstyle.alignment());
240 	invalid = true;
241 	m_currentShadow = newShadow;
242 }
243 /*
244 static void debugLineLayout(const StoryText& itemText, const LineSpec& line)
245 {
246 	QFile debugFile(QDir::homePath() + "/Desktop/debug_line.csv");
247 	debugFile.open(QIODevice::WriteOnly);
248 
249 	QTextStream stream(&debugFile);
250 	stream.setRealNumberNotation(QTextStream::FixedNotation);
251 	stream.setRealNumberPrecision(7);
252 
253 	stream << "xoffset"  << "\t";
254 	stream << "yoffset"  << "\t";
255 	stream << "xadvance" << "\t";
256 	stream << "yadvance" << "\t";
257 	stream << "scaleH"   << "\t";
258 	stream << "scaleV"   << "\t";
259 	stream << "\n";
260 
261 	for (int zc = line.firstChar; zc < line.lastChar; ++zc)
262 	{
263 		const ScText* item = itemText.item(zc);
264 
265 		stream << item->glyph.xoffset  << "\t";
266 		stream << item->glyph.yoffset  << "\t";
267 		stream << item->glyph.xadvance << "\t";
268 		stream << item->glyph.yadvance << "\t";
269 		stream << item->glyph.scaleH   << "\t";
270 		stream << item->glyph.scaleV   << "\t";
271 		stream << "\n";
272 	}
273 
274 	debugFile.close();
275 }
276 
277 static void dumpIt(const ParagraphStyle& pstyle, QString indent = QString("->"))
278 {
279 	QString db = QString("%6%1/%2 @ %3: %4--%5 linespa%6: %7 align%8")
280 		   .arg(pstyle.name())
281 		   .arg(pstyle.parent())
282 		   .arg( (unsigned long int) &pstyle)
283 		   .arg(pstyle.leftMargin())
284 		   .arg(pstyle.rightMargin())
285 		   .arg(indent)
286 		   .arg(pstyle.lineSpacingMode())
287 		   .arg(pstyle.lineSpacing())
288 		   .arg(pstyle.alignment());
289 	qDebug() << db;
290 	static QString more("  ");
291 	if (pstyle.hasParent())
292 		dumpIt(*dynamic_cast<const ParagraphStyle*>(pstyle.parentStyle()), more + indent);
293 }
294 */
295 static const bool legacy = true;
296 
297 /*
298 static void layoutDropCap(GlyphLayout layout, double curX, double curY, double offsetX, double offsetY, double dropCapDrop)
299 {
300 }
301 */
302 
303 
304 enum TabStatus {
305 	TabNONE    = 0,
306 	TabLEFT    = TabNONE,
307 	TabRIGHT   = 1,
308 	TabPOINT   = 2,
309 	TabCOMMA   = 3,
310 	TabCENTER  = 4
311 };
312 
313 
314 /**
315 fields which describe what type of tab is currently active
316  */
317 struct TabControl {
318 	bool         active;
319 	int          status;
320 	double       xPos;
321 	QChar        fillChar;
322 	GlyphLayout* tabGlyph;
323 };
324 
325 class LineSpec
326 {
327 	public:
LineSpec()328 	LineSpec()
329 	{
330 		x = 0.0;
331 		y = 0.0;
332 		width  = 0.0;
333 		height = 0.0;
334 		ascent = 0.0;
335 		descent = 0.0;
336 		colLeft = 0.0;
337 		firstCluster = 0;
338 		lastCluster = 0;
339 		naturalWidth = 0.0;
340 		isFirstLine = false;
341 	}
342 
343 	qreal x;
344 	qreal y;
345 	qreal width;
346 	qreal height;
347 	qreal ascent;
348 	qreal descent;
349 	qreal colLeft;
350 
351 	int firstCluster;
352 	int lastCluster;
353 	qreal naturalWidth;
354 	bool isFirstLine;
355 };
356 
357 /**
358 fields which describe how the current line is placed into the frame
359 */
360 struct LineControl {
361 	LineSpec lineData;
362 	QList<GlyphCluster> glyphs;
363 	bool     isEmpty;
364 	int      hyphenCount;
365 	double   colWidth;
366 	double   colGap;
367 	double   colLeft;
368 	double   colRight;
369 	int      column;
370 	bool     startOfCol;
371 	bool     hasDropCap;
372 	bool     afterOverflow;
373 	bool     addLine;
374 	bool     recalculateY;
375 	bool     lastInRowLine;
376 	bool     addLeftIndent;
377 	bool     wasFirstInRow;
378 	double   leftIndent;
379 	double   rightIndent;
380 	double   rightMargin;
381 	double   mustLineEnd;
382 	int      restartIndex;  //index of glyph run where line computing should be restarted
383 	int      restartRowIndex;  //index of glyph run where row of text is started
384 	double   restartX; //starting X position of line if must be restarted
385 	double   rowDesc;
386 
387 	double   xPos;
388 	double   yPos;
389 	int      breakIndex;
390 	double   breakXPos;
391 
392 	double   maxShrink;
393 	double   maxStretch;
394 	ScribusDoc *doc;
395 	ITextContext* context;
396 
397 	/// remember frame dimensions and offsets
LineControlLineControl398 	LineControl(double w, double h, const MarginStruct& extra, double lCorr, ScribusDoc* d, ITextContext* ctx, double colwidth, double colgap)
399 		: hasDropCap(false)
400 		, doc(d), context(ctx)
401 	{
402 		insets = extra;
403 		lineCorr = lCorr;
404 		frameWidth = w;
405 		frameHeight = h;
406 		column = 0;
407 		colWidth = colwidth;
408 		colGap = colgap;
409 		hyphenCount = 0;
410 		isEmpty = true;
411 		colLeft = insets.left() + lineCorr;
412 		colRight = colLeft + colWidth;
413 		startOfCol = true;
414 		afterOverflow = false;
415 		addLine = false;
416 		recalculateY = false;
417 		lastInRowLine = false;
418 		addLeftIndent = false;
419 		wasFirstInRow = false;
420 		leftIndent = 0.0;
421 		rightIndent = 0.0;
422 		rightMargin = 0.0;
423 		mustLineEnd = false;
424 		restartIndex = 0;
425 		restartRowIndex = 0;
426 		restartX = 0.0;
427 		rowDesc = 0.0;
428 		xPos = 0.0;
429 		yPos = 0.0;
430 		breakIndex = -1;
431 		breakXPos = 0.0;
432 		maxShrink = 0.0;
433 		maxStretch = 0.0;
434 	}
435 
436 	/// move position to next column
nextColumnLineControl437 	void nextColumn(TextLayout &textLayout)
438 	{
439 		startOfCol = true;
440 		if (textLayout.story()->defaultStyle().direction() == ParagraphStyle::RTL)
441 			colLeft = textLayout.frame()->width() - insets.right() - ((colWidth * (column + 1)) + (colGap * column));
442 		else
443 			colLeft = (colWidth + colGap) * column + insets.left() + lineCorr;
444 		textLayout.addColumn(colLeft, colWidth);
445 		//now colRight is REAL column right edge
446 		colRight = colLeft + colWidth;
447 		if (legacy)
448 			colRight += lineCorr;
449 		xPos = colLeft;
450 		yPos = insets.top() + lineCorr;
451 		lineData.colLeft = colLeft;
452 	}
453 
isEndOfColLineControl454 	bool isEndOfCol(double morespace = 0)
455 	{
456 		return yPos + morespace + insets.bottom() + lineCorr > frameHeight;
457 	}
458 
459 	/**
460 		init fields for a new line at current position
461 	 */
startLineLineControl462 	void startLine(int firstCluster)
463 	{
464 		glyphs.clear();
465 		isEmpty = true;
466 		lineData.x = xPos;
467 		lineData.y = yPos;
468 		lineData.firstCluster = firstCluster;
469 		lineData.lastCluster = 0;
470 		lineData.ascent = 0.0;
471 		lineData.descent = 0.0;
472 		lineData.width  = 0.0;
473 		lineData.height = 0.0;
474 		lineData.naturalWidth = 0.0;
475 		lineData.colLeft = colLeft;
476 		breakIndex = -1;
477 		breakXPos = 0.0;
478 		maxShrink = 0.0;
479 		maxStretch = 0.0;
480 		leftIndent = 0.0;
481 		rightIndent = 0.0;
482 		rightMargin = 0.0;
483 		rowDesc = 0.0;
484 	}
485 
486 
487 	/// called when glyphs are placed on the line
rememberShrinkStretchLineControl488 	void rememberShrinkStretch(QChar ch, double wide, const ParagraphStyle& style)
489 	{
490 		if (SpecialChars::isExpandingSpace(ch))
491 			maxShrink += (1 - style.minWordTracking()) * wide;
492 		else
493 		{
494 			maxShrink += (1 - style.minGlyphExtension()) * wide;
495 		}
496 		maxStretch += (style.maxGlyphExtension() - 1) * wide;
497 	}
498 
499 	/// called when a possible break is passed
rememberBreakLineControl500 	void rememberBreak(int index, double pos, double morespace = 0)
501 	{
502 		if (pos > colRight - morespace)
503 		{
504 			// only look for the first break behind the right edge
505 			//maxShrink = 0;
506 
507 			// check if we already have a better break
508 			if (breakIndex >= 0)
509 			{
510 				double oldLooseness = qAbs(colRight - breakXPos);
511 
512 				double newLooseness = pos - colRight;
513 				if (newLooseness >= oldLooseness)
514 					return;
515 			}
516 		}
517 		breakXPos = pos;
518 		breakIndex = index;
519 	}
520 
521 	/// called when a mandatory break is found
breakLineLineControl522 	void breakLine(int last)
523 	{
524 		breakIndex = last;
525 		breakXPos  = lineData.x;
526 
527 		for (int i = 0; i <= breakIndex - lineData.firstCluster; i++)
528 			breakXPos += glyphs.at(i).width();
529 		// #8194, #8717 : update line ascent and descent with sensible values
530 		// so that endOfLine() returns correct result
531 		updateHeightMetrics();
532 		// #9060 : update line offset too
533 	//	updateLineOffset(itemText, style, offsetPolicy);
534 	}
535 
536 	/// use the last remembered break to set line width and itemrange
finishLineLineControl537 	void finishLine(double endX)
538 	{
539 		lineData.lastCluster = breakIndex;
540 		lineData.naturalWidth = breakXPos - lineData.x;
541 		lineData.width = endX - lineData.x;
542 		maxShrink = maxStretch = 0;
543 	}
544 
restartRowLineControl545 	int restartRow(bool recalcY)
546 	{
547 		if (recalcY)
548 			yPos++;
549 		recalculateY = recalcY;
550 		xPos = restartX = colLeft;
551 		startLine(restartRowIndex);
552 		addLeftIndent = true;
553 		afterOverflow = false;
554 		return restartRowIndex - 1;
555 	}
556 
restartLineLineControl557 	int restartLine(bool recalcY, bool add)
558 	{
559 		recalculateY = recalcY;
560 		addLine = add;
561 		xPos = restartX;
562 		startLine(restartIndex);
563 		afterOverflow = false;
564 		return restartIndex - 1;
565 	}
566 
isEndOfLineLineControl567 	bool isEndOfLine(double moreSpace = 0)
568 	{
569 		bool res;
570 		if (legacy)
571 			res = ceil(xPos + lineCorr - maxShrink) + ceil(moreSpace) >= floor(colRight);
572 		else
573 			res = ceil(xPos - maxShrink)  + ceil(moreSpace) >= floor(colRight);
574 		return res;
575 	}
576 
577 	/// Keep old endOfLine code for reference
578 	/*double endOfLine_old(const QRegion& shape, const QTransform& pf2, double morespace, int Yasc, int Ydesc)
579 	{
580 		// if we aren't restricted further, we'll end at this maxX:
581 		double maxX = colRight - morespace;
582 		if (legacy) maxX -= lineCorr;
583 
584 		double StartX = floor(qMax(line.x, qMin(maxX, breakXPos - maxShrink - 1)) - 1);
585 		int xPos  = static_cast<int>(ceil(maxX));
586 
587 		QPoint  pt12 (xPos, Yasc);
588 		QPoint  pt22 (xPos, Ydesc);
589 		QRect   pt(pt12,pt22);
590 		QRegion region;
591 
592 		double EndX2 = StartX;
593 		double Interval = 0.25;
594 		do {
595 			int xP = static_cast<int>(ceil(EndX2 + morespace));
596 			pt.moveTopLeft(QPoint(xP, Yasc));
597 			region = QRegion(pf2.mapToPolygon(pt)).subtracted(shape);
598 			if (!region.isEmpty())
599 				break;
600 			EndX2 += Interval;
601 		} while ((EndX2 < maxX) && region.isEmpty());
602 
603 		return qMin(EndX2, maxX);
604 	}*/
605 
606 	/// find x position where this line must end
endOfLineLineControl607 	double endOfLine(const QRegion& shape, double morespace, int yAsc, int yDesc)
608 	{
609 		// if we aren't restricted further, we'll end at this maxX:
610 		double maxX = colRight - morespace;
611 		if (legacy) maxX -= lineCorr;
612 
613 		double StartX = floor(qMax(lineData.x, qMin(maxX, breakXPos - maxShrink - 1)) - 1);
614 		StartX = qMax(0.0, StartX);
615 
616 		int xPos  = static_cast<int>(ceil(maxX));
617 		QPoint  pt12 (xPos, yAsc);
618 		QPoint  pt22 (xPos, yDesc);
619 
620 		QPolygon p;
621 		p.append (QPoint (StartX, yAsc));
622 		p.append (QPoint (StartX, yDesc));
623 		p.append (pt12);
624 		p.append (pt22);
625 		// check if something gets in the way
626 		QRegion lineI = shape.intersected (p.boundingRect());
627 		// if the intersection only has 1 rectangle, then nothing gets in the way
628 		if (lineI.rectCount() == 1)
629 		{
630 			int   cPos = static_cast<int>(ceil(StartX + morespace));
631 			QRect cRect (QPoint(cPos, yAsc), QPoint(cPos, yDesc));
632 			QRegion qr2 = QRegion(cRect).subtracted(shape);
633 			if (qr2.isEmpty()) // qr2 == 0 <=> cRect subset of shape
634 			{
635 				QRegion::const_iterator rect = lineI.cbegin();
636 				double  mx = qMax(rect->left(), rect->right()) /*- pf2.dx()*/;
637 				int steps  = static_cast<int>((mx - StartX - morespace - 2) / 0.25);
638 				if (steps > 0)
639 				{
640 					StartX += steps * 0.25;
641 				}
642 			}
643 		}
644 
645 		QRect   pt(pt12, pt22);
646 
647 		double EndX2 = StartX;
648 		double Interval = 0.25;
649 		do {
650 			int xP = static_cast<int>(ceil(EndX2 + morespace));
651 			pt.moveTopLeft(QPoint(xP, yAsc));
652 			if (!regionContainsRect(shape, pt))
653 				break;
654 			EndX2 += Interval;
655 		} while ((EndX2 < maxX) && regionContainsRect(shape, pt));
656 
657 		/*double oldEndX2 = endOfLine_old(shape, pf2, morespace, yAsc, yDesc);
658 		if (oldEndX2 != qMin(EndX2, maxX))
659 		{
660 			qDebug() << "Different EndX : " << oldEndX2 << " (old) " << EndX2 << " (new) ";
661 		}*/
662 
663 		return qMin(EndX2, maxX);
664 	}
665 
updateHeightMetricsLineControl666 	void updateHeightMetrics()
667 	{
668 		lineData.ascent = lineData.descent = 0;
669 		if (glyphs.isEmpty())
670 			return;
671 		const CharStyle& cStyle(glyphs.at(0).style());
672 		double scaleV = cStyle.scaleV() / 1000.0;
673 		double offset = (cStyle.fontSize() / 10) * (cStyle.baselineOffset() / 1000.0);
674 		lineData.ascent = cStyle.font().ascent(cStyle.fontSize()/10.00) * scaleV + offset;
675 		lineData.descent = cStyle.font().descent(cStyle.fontSize()/10.00) * scaleV - offset;
676 	}
677 
678 // yPos should not be changed when all line is already calculated - at new y position there can be overflow!!!
679 // edit: can't happen as it should only move upwards, and this is covered by the calculations done.
680 //void updateLineOffset(const StoryText& itemText, const ParagraphStyle& style, FirstLineOffsetPolicy offsetPolicy)
681 //{
682 //	if (charsInLine <= 0)
683 //		return;
684 //	if ((!hasDropCap) && (startOfCol) && (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing))
685 //	{
686 //		//FIXME: use glyphs, not chars
687 //		double firstasce = itemText.charStyle(line.firstChar).font().ascent(itemText.charStyle(line.firstChar).fontSize() / 10.0);
688 //		double adj (0.0);
689 //		double currasce (this->getLineAscent(itemText));
690 //		if (offsetPolicy == FLOPRealGlyphHeight)
691 //		{
692 //			adj = firstasce - currasce;
693 //		}
694 //		else if (offsetPolicy == FLOPFontAscent)
695 //		{
696 //			adj = 0.0;
697 //		}
698 //		else if (offsetPolicy == FLOPLineSpacing)
699 //		{
700 //			adj = firstasce - style.lineSpacing();
701 //		}
702 //		line.ascent = currasce;
703 //		line.y -= adj;
704 //		yPos -= adj;
705 //	}
706 //	else if ((!startOfCol) && (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing))
707 //	{
708 //		QChar ch = itemText.text(line.firstChar);
709 //		double firstasce = style.lineSpacing();
710 //		double currasce  = getLineHeight(itemText);
711 //		double adj = firstasce - currasce;
712 //		qDebug() << QString("move2 line %1.. down by %2").arg(current.line.firstChar).arg(-adj);
713 //		line.ascent = currasce;
714 //		line.y -= adj;
715 //		yPos -= adj;
716 //	}
717 //}
718 
719 	/// called when line length is known and line is to be justified
justifyLineLineControl720 	void justifyLine(const ParagraphStyle& style)
721 	{
722 
723 		double glyphNatural = 0;
724 		double spaceNatural = 0;
725 		double glyphExtension;
726 		double spaceExtension;
727 		int spaceInsertion = 0;
728 		double imSpace = -1;
729 		int trackingInsertion = 0;
730 		double trackingAmount = 0;
731 
732 		int glyphsCount = lineData.lastCluster - lineData.firstCluster + 1;
733 
734 		for (int i = 0; i < glyphsCount; ++i)
735 		{
736 			const GlyphCluster& glyphCluster = glyphs.at(i);
737 			if (!glyphCluster.hasFlag(ScLayout_ExpandingSpace))
738 			{
739 				glyphNatural += glyphCluster.width();
740 			}
741 			else if (!glyphCluster.hasFlag(ScLayout_SuppressSpace))
742 			{
743 				spaceNatural += glyphCluster.width();
744 				if (imSpace < 0.0 || imSpace > glyphCluster.width())
745 					imSpace = glyphCluster.width();
746 			}
747 			if (i != 0 && glyphCluster.hasFlag(ScLayout_ImplicitSpace))
748 			{
749 				spaceInsertion += 1;
750 			}
751 			if (i != glyphsCount && glyphCluster.hasFlag(ScLayout_JustificationTracking))
752 			{
753 				trackingInsertion += 1;
754 			}
755 		}
756 
757 		imSpace /= 2;
758 
759 		// decision: prio 1: stretch glyph;  prio 2: insert spaces;  prio 3: stretch spaces
760 		if (lineData.width < spaceNatural + glyphNatural * style.minGlyphExtension() && spaceNatural > 0)
761 		{
762 			glyphExtension = style.minGlyphExtension() - 1;
763 			spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension) ) / spaceNatural - 1;
764 			imSpace = 0;
765 		}
766 		else if (lineData.width < spaceNatural + glyphNatural * style.maxGlyphExtension() && glyphNatural > 0)
767 		{
768 			spaceExtension = 0;
769 			glyphExtension = (lineData.width - spaceNatural) / glyphNatural - 1;
770 			imSpace = 0;
771 		}
772 		else
773 		{
774 			glyphExtension = style.maxGlyphExtension() - 1;
775 			if (spaceInsertion)
776 			{
777 				double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural;
778 				if (imSpace > 0)
779 				{
780 					if (remaining / spaceInsertion < imSpace)
781 					{
782 						imSpace = remaining / spaceInsertion;
783 						spaceExtension = 0;
784 					}
785 					else
786 					{
787 						spaceExtension = (remaining + spaceNatural) / (spaceNatural + spaceInsertion * imSpace) - 1;
788 						imSpace *= spaceExtension + 1;
789 					}
790 				}
791 				else
792 				{
793 					imSpace = remaining / spaceInsertion;
794 					spaceExtension = 0;
795 				}
796 			}
797 			else
798 			{
799 				if (spaceNatural > 0)
800 					spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension)) / spaceNatural - 1;
801 				else
802 					spaceExtension = 0;
803 
804 				if (trackingInsertion && (spaceExtension == 0.0 || spaceExtension >= 20.0))
805 				{
806 					if (spaceExtension == 0.0)
807 					{
808 						double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural;
809 						trackingAmount = remaining / trackingInsertion;
810 					}
811 					else
812 					{
813 						spaceExtension = qMin(20.0, spaceExtension);
814 						double remaining = lineData.width - glyphNatural * (1 + glyphExtension) - spaceNatural;
815 						remaining -= spaceExtension * spaceNatural;
816 						trackingAmount = remaining / trackingInsertion;
817 						if (trackingAmount > imSpace)
818 						{
819 							trackingAmount = imSpace;
820 							spaceExtension = (lineData.width - glyphNatural * (1 + glyphExtension) - trackingInsertion * trackingAmount) / spaceNatural - 1;
821 						}
822 					}
823 				}
824 			}
825 		}
826 
827 		double glyphScale = 1 + glyphExtension;
828 		double naturalWidth = 0;
829 
830 	/*
831 		qDebug() << QString("justify: line = %7 natural = %1 + %2 = %3 (%4); spaces + %5%%; min=%8; glyphs + %6%%; min=%9")
832 			   .arg(spaceNatural).arg(glyphNatural).arg(spaceNatural+glyphNatural).arg(line.naturalWidth)
833 			   .arg(spaceExtension).arg(glyphExtension).arg(line.width)
834 			   .arg(style.minWordTracking()).arg(style.minGlyphExtension());
835 		*/
836 
837 		int startItem = 0;
838 		if (glyphs[startItem].hasFlag(ScLayout_DropCap))
839 		{
840 			startItem++;
841 			naturalWidth += glyphs[startItem].width();
842 		}
843 		// distribute whitespace on spaces and glyphs
844 		for (int i = startItem; i < glyphsCount; ++i)
845 		{
846 			GlyphCluster& glyphCluster = glyphs[i];
847 			double wide = glyphCluster.width();
848 			if (!glyphCluster.hasFlag(ScLayout_ExpandingSpace))
849 			{
850 				glyphCluster.setScaleH(glyphCluster.scaleH() * glyphScale);
851 				glyphCluster.xoffset *= glyphScale;
852 			}
853 			else if (!glyphCluster.hasFlag(ScLayout_SuppressSpace))
854 			{
855 				glyphCluster.extraWidth += (wide * spaceExtension);
856 			}
857 			if (i != 0 && glyphCluster.hasFlag(ScLayout_ImplicitSpace))
858 			{
859 				GlyphCluster& lastRun = glyphs[i - 1];
860 				lastRun.extraWidth += imSpace;
861 			}
862 			if (i != glyphsCount && trackingAmount != 0 && glyphCluster.hasFlag(ScLayout_JustificationTracking))
863 			{
864 				glyphCluster.extraWidth += trackingAmount;
865 			}
866 			naturalWidth += glyphCluster.width();
867 		}
868 		lineData.naturalWidth = naturalWidth;
869 
870 		if ((style.alignment() == ParagraphStyle::Extended) &&
871 			(style.direction() == ParagraphStyle::RTL))
872 		{
873 			double offset = lineData.width - lineData.naturalWidth;
874 			if (offset > 1e-6)
875 				indentLine(style, offset);
876 		}
877 	}
878 
879 	/// called when linelength is known and line is not justified
indentLineLineControl880 	void indentLine(const ParagraphStyle& style, double leftIndent)
881 	{
882 		if (lineData.naturalWidth > lineData.width)
883 		{
884 			justifyLine(style);
885 		}
886 		if (leftIndent > 0)
887 		{
888 			lineData.x += leftIndent;
889 			lineData.width -= leftIndent;
890 		}
891 	}
892 
893 	/**
894 	 Clones the tab fill char as often as necssary after all distances are known
895 	 */
fillInTabLeadersLineControl896 	void fillInTabLeaders()
897 	{
898 		// fill in tab leaders
899 		for (int i = 0; i < glyphs.count(); ++i)
900 		{
901 			GlyphCluster& glyphCluster = glyphs[i];
902 			if (glyphCluster.hasFlag(ScLayout_TabLeaders))
903 			{
904 				const CharStyle& charStyle(glyphCluster.style());
905 				GlyphLayout tglyph = glyphCluster.glyphs().last();
906 				double width = glyphCluster.width();
907 				double wt = charStyle.font().glyphWidth(tglyph.glyph, charStyle.fontSize() * tglyph.scaleV / 10.0);
908 				int count = static_cast<int>(width / wt);
909 				if (count > 0)
910 					glyphCluster.glyphs().clear();
911 				for(int cx = 0; cx < count; ++cx)
912 				{
913 					GlyphLayout more = tglyph;
914 					more.xadvance = 0;
915 					if (cx != 0)
916 						more.xoffset = (width / count) * cx;
917 					glyphCluster.append(more);
918 				}
919 				glyphCluster.glyphs().last().xadvance = width / glyphCluster.scaleH();
920 			}
921 		}
922 	}
923 
924 
925 	//defined but not used
926 	///// calculate how much the first char should stick out to the left
927 	//double opticalLeftMargin(const StoryText& itemText)
928 	//{
929 	//	int b = line.firstChar;
930 	//	while (b < line.lastChar && (itemText.flags(b) & ScLayout_SuppressSpace))
931 	//		   ++b;
932 	//
933 	//	double chs = itemText.charStyle(b).fontSize() * (itemText.charStyle(b).scaleH() / 1000.0);
934 	//	QChar chr = itemText.text(b);
935 	//	double leftCorr = itemText.charStyle(b).font().realCharWidth(chr, chs / 10.0);
936 	//	if (QString("'´`").indexOf(chr) >= 0
937 	//		|| chr == QChar(0x2018) // quote 6
938 	//		|| chr == QChar(0x2019) // quote 9
939 	//		|| chr == QChar(0x201a) // lower quote 9
940 	//		|| chr == QChar(0x201b) // upper reversed 9 6
941 	//		|| chr == QChar(0x2039) // single guillemet <
942 	//		|| chr == QChar(0x203a) // single guillemet >
943 	//		)
944 	//		leftCorr *= -0.7;
945 	//	else if (QString("\"").indexOf(chr) >= 0
946 	//			 || chr == QChar(0x00ab) // guillemet <<
947 	//			 || chr == QChar(0x00bb) // guillemet >>
948 	//			 || chr == QChar(0x201c) // quote 66
949 	//			 || chr == QChar(0x201d) // quote 99
950 	//			 || chr == QChar(0x201e) // lower quote 99
951 	//			 || chr == QChar(0x201f) // upper reversed 99
952 	//			 )
953 	//		leftCorr *= -0.5;
954 	//	else {
955 	//		leftCorr = itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0, chr);
956 	//		leftCorr -= itemText.charStyle(b).font().charWidth(QChar(' '), chs / 10.0);
957 	////				double leftCorr2 = itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0, chr);
958 	////				leftCorr2 -= itemText.charStyle(a).font().charWidth(QChar('K'), chs / 10.0);
959 	////				leftCorr = qMin(leftCorr, leftCorr2);
960 	//	}
961 	//	return leftCorr;
962 	//}
963 
964 	/// calculate how much the last char should stick out to the right
opticalRightMarginLineControl965 	double opticalRightMargin(const StoryText& itemText)
966 	{
967 		int b = lineData.lastCluster - lineData.firstCluster;
968 		while (b > 0 &&
969 			   (SpecialChars::isBreakingSpace(itemText.text(glyphs[b].lastChar())) ||
970 				SpecialChars::isBreak(itemText.text(glyphs[b].lastChar())))
971 			   )
972 			--b;
973 		if (b >= 0)
974 		{
975 			const CharStyle& style = glyphs[b].style();
976 			const ScFace& font = style.font();
977 			double chs = style.fontSize() * (style.scaleH() / 1000.0);
978 			QChar chr = itemText.text(glyphs[b].lastChar());
979 			double rightCorr;
980 			if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible))
981 				rightCorr = font.hyphenWidth(style, chs / 10.0);
982 			else
983 				rightCorr = font.glyphBBox(font.char2CMap(chr.unicode()), chs / 10.0).width;
984 			if (glyphs[b].hasFlag(ScLayout_SoftHyphenVisible)
985 				|| QString("-,.`´'~").indexOf(chr) >= 0
986 				|| chr == QChar(0x2010)
987 				|| chr == QChar(0x2018)
988 				|| chr == QChar(0x2019)
989 				|| chr == QChar(0x201a)
990 				|| chr == QChar(0x201b)
991 				|| chr == QChar(0x2039)
992 				|| chr == QChar(0x203a)
993 				|| chr == QChar(0x2032) // PRIME
994 				)
995 				rightCorr *= 0.7;
996 			else if (QString(";:\"").indexOf(chr) >= 0
997 					 || chr == QChar(0x00ab)
998 					 || chr == QChar(0x00bb)
999 					 || chr == QChar(0x201c)
1000 					 || chr == QChar(0x201d)
1001 					 || chr == QChar(0x201e)
1002 					 || chr == QChar(0x201f)
1003 					 || chr == QChar(0x2013) // EN DASH
1004 					 || chr == QChar(0x2033) // double prime
1005 					 )
1006 				rightCorr *= 0.5;
1007 			else {
1008 #if 0
1009 				// FIXME HOST: is the kerning with "." a realy reliable way to check this?
1010 				rightCorr = chStyle.font().realCharWidth(chr, chs / 10.0);
1011 				rightCorr -= chStyle.font().charWidth(chr, chs / 10.0, QChar('.'));
1012 #else
1013 				rightCorr = 0;
1014 #endif
1015 			}
1016 			return rightCorr;
1017 		}
1018 		return 0.0;
1019 	}
1020 
createLineBoxLineControl1021 	LineBox* createLineBox()
1022 	{
1023 		LineBox* result = new LineBox();
1024 		result->moveTo(lineData.x - colLeft, lineData.y - lineData.ascent);
1025 		result->setWidth(lineData.width);
1026 		result->setAscent(lineData.ascent);
1027 		result->setDescent(lineData.descent);
1028 		for (const GlyphCluster& run : ShapedTextFeed::putInVisualOrder(glyphs,   0, lineData.lastCluster - lineData.firstCluster + 1))
1029 		{
1030 			addBox(result, run);
1031 //			qDebug() << "cluster" << run.firstChar() << ".." << run.lastChar() << "@" << run.visualIndex();
1032 		}
1033 		return result;
1034 	}
1035 
addBoxLineControl1036 	void addBox(LineBox *lineBox, const GlyphCluster& run)
1037 	{
1038 		Box* result;
1039 		if (run.object().getPageItem(doc))
1040 		{
1041 			result = new ObjectBox(run, context);
1042 			QRectF bBox = context->getVisualBoundingBox(run.object());
1043 			if (run.hasFlag(ScLayout_DropCap))
1044 				result->setAscent(bBox.height() * run.scaleV() - run.yoffset);
1045 			else
1046 				result->setAscent(bBox.height());
1047 			result->setDescent(0);
1048 		}
1049 		else
1050 		{
1051 			result = new GlyphBox(run);
1052 			result->setAscent(lineBox->ascent());
1053 			result->setDescent(lineBox->descent());
1054 		}
1055 		lineBox->addBox(result);
1056 	}
1057 
1058 private:
1059 	double frameWidth;
1060 	double frameHeight;
1061 	MarginStruct insets;
1062 	double lineCorr;
1063 };
1064 
findRealOverflowEnd(const QRegion & shape,QRect pt,double maxX)1065 static double findRealOverflowEnd(const QRegion& shape, QRect pt, double maxX)
1066 {
1067 	while (!regionContainsRect(shape, pt) && pt.right() < maxX)
1068 		pt.translate(1, 0);
1069 	if (pt.right() >= maxX)
1070 		return maxX;
1071 	return pt.left() + 0.5;
1072 }
1073 
adjustToBaselineGrid(const LineControl & control,PageItem * item,int OwnPage)1074 static double adjustToBaselineGrid (const LineControl &control, PageItem *item, int OwnPage)
1075 {
1076 	double by = item->yPos();
1077 	if (OwnPage != -1)
1078 		by = by - item->doc()->Pages->at(OwnPage)->yOffset();
1079 	int ol1 = qRound((by + control.yPos - item->doc()->guidesPrefs().offsetBaselineGrid) * 10000.0);
1080 	int ol2 = static_cast<int>(ol1 / item->doc()->guidesPrefs().valueBaselineGrid);
1081 //						qDebug() << QString("baseline adjust: y=%1->%2").arg(current.yPos).arg(ceil(  ol2 / 10000.0 ) * item->doc()->typographicSettings.valueBaselineGrid + item->doc()->typographicSettings.offsetBaselineGrid - by);
1082 
1083 	return ceil(  ol2 / 10000.0 ) * item->doc()->guidesPrefs().valueBaselineGrid + item->doc()->guidesPrefs().offsetBaselineGrid - by;
1084 }
1085 
nextAutoTab(const LineControl & current,PageItem * item)1086 static double nextAutoTab (const LineControl &current, PageItem *item)
1087 {
1088 	double dtw = item->doc()->itemToolPrefs().textTabWidth;
1089 	if (current.xPos == current.colLeft)
1090 		return current.colLeft + dtw;
1091 	double res = current.colLeft + ceil ((current.xPos - current.colLeft) / dtw) * dtw;
1092 	if (res == current.xPos) res += dtw;
1093 	return res;
1094 }
1095 
1096 //cezaryece: I remove static statement as this function is used also by PageItem_NoteFrame
calculateLineSpacing(const ParagraphStyle & style,PageItem * item)1097 double calculateLineSpacing (const ParagraphStyle &style, PageItem *item)
1098 {
1099 	if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
1100 	{
1101 		double autoLS = static_cast<double>(item->doc()->typographicPrefs().autoLineSpacing) / 100.0;
1102 		return (style.charStyle().font().height(style.charStyle().fontSize() / 10.0) * autoLS);
1103 	}
1104 	if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
1105 		return item->doc()->guidesPrefs().valueBaselineGrid;
1106 	return style.lineSpacing();
1107 }
1108 
1109 
1110 // This assumes that layout() ran on the previous page and set the incomplete* vars
1111 // It also clears the incomplete* vars, and changes the starting position for this frame
1112 // The incomplete* vars are used to ensure that we don't run into an endless loop
1113 // This setup won't cause any problems, as the only way for the starting position to change
1114 // back is if the previous frame's layout() runs again, but if it does, it also sets the
1115 // incomplete* vars for us
moveLinesFromPreviousFrame()1116 bool PageItem_TextFrame::moveLinesFromPreviousFrame ()
1117 {
1118 	PageItem_TextFrame* prev = dynamic_cast<PageItem_TextFrame*>(m_backBox);
1119 	if (!prev)
1120 		return false;
1121 	if (!prev->incompleteLines)
1122 		return false;   // no incomplete lines - nothing to do
1123 	int pos = textLayout.endOfFrame() - 1;
1124 	QChar lastChar = itemText.text (pos);
1125 	// qDebug()<<"pos is"<<pos<<", length is"<<itemText.length()<<", incomplete is "<<prev->incompleteLines;
1126 	if ((pos != itemText.length() - 1) && (!SpecialChars::isBreak (lastChar, true)))
1127 		return false;  // the paragraph isn't ending yet
1128 	int lines = textLayout.lines();  // lines added to the current frame
1129 
1130 	ParagraphStyle style = itemText.paragraphStyle (pos);
1131 	int need = style.keepLinesEnd () + 1;
1132 	int prevneed = style.keepLinesStart () + 1;
1133 	if (lines >= need)
1134 	{
1135 		prev->incompleteLines = 0;   // so that further paragraphs don't pull anything
1136 		return false;   // we have enough lines
1137 	}
1138 
1139 	// too few lines - we need to pull some from the previous page
1140 	int pull = need - lines;
1141 	// if pulling the lines would lead to the original frame having too few, pull the whole paragraph
1142 	if (prev->incompleteLines - pull < prevneed)
1143 		pull = prev->incompleteLines;
1144 	// qDebug()<<"pulling"<<pull<<"lines;
1145 	// Okay, move the starting/ending character
1146 	int startingPos = prev->incompletePositions[prev->incompleteLines - pull];
1147 	for (int i = 0; i < pull; ++i)
1148 		prev->textLayout.removeLastLine();
1149 	firstChar = prev->m_maxChars = startingPos;
1150 	// keep the remaining incomplete lines flagged as such
1151 	// this ensures that if pulling one line won't be enough, the subsequent call to layout() will pull more
1152 	prev->incompleteLines -= pull;
1153 
1154 	return true;
1155 }
1156 
1157 // called at the end of a frame or column
adjustParagraphEndings()1158 void PageItem_TextFrame::adjustParagraphEndings ()
1159 {
1160 	// More text to go - let's apply paragraph flowing options - orphans/widows, etc
1161 	int start = textLayout.startOfFrame();
1162 	int end = textLayout.endOfFrame() - 1;
1163 	if ((start > end) || (end >= itemText.length() - 1))
1164 		return;
1165 
1166 	ParagraphStyle style = itemText.paragraphStyle (end);
1167 	int paragraphStart = itemText.prevParagraph (end) + 1;
1168 	QChar lastChar = itemText.text (end);
1169 	bool keepWithNext = style.keepWithNext() && (lastChar == SpecialChars::PARSEP);
1170 	if (keepWithNext || (!SpecialChars::isBreak (lastChar, true)))
1171 	{
1172 		// paragraph continues in the next frame, or needs to be kept with the next one
1173 		// check how many lines are in this frame
1174 		int lineStart = textLayout.startOfLine (end);
1175 		incompleteLines = 1;
1176 		incompletePositions.prepend (lineStart);
1177 		while (lineStart > paragraphStart)
1178 		{
1179 			lineStart = textLayout.startOfLine (lineStart - 1);
1180 			incompleteLines++;
1181 			incompletePositions.prepend (lineStart);
1182 		}
1183 		int need = style.keepLinesStart () + 1;
1184 		if (style.keepTogether())
1185 			need = incompleteLines;
1186 		int pull = 0;
1187 		if (style.keepTogether() || (incompleteLines < need))
1188 			pull = incompleteLines;
1189 		// if we need to keep it with the next one, pull one line. Next frame layouting
1190 		// will pull more from us if it proves necessary.
1191 		if (keepWithNext && (!pull))
1192 			pull = 1;
1193 
1194 		if (pull)
1195 		{
1196 			qDebug() << "pulling" << pull << "lines";
1197 			// push this paragraph to the next frame
1198 			for (int i = 0; i < pull; ++i)
1199 				textLayout.removeLastLine();
1200 			m_maxChars = incompletePositions[incompleteLines-pull];
1201 			incompleteLines = 0;
1202 			incompletePositions.clear();
1203 		}
1204 	}
1205 }
1206 
layout()1207 void PageItem_TextFrame::layout()
1208 {
1209 //	qDebug()<<"==Layout==" << itemName() ;
1210 // 	printBacktrace(24);
1211 	if (m_backBox != nullptr) {
1212 //		qDebug("textframe: len=%d, going back", itemText.length());
1213 		PageItem_TextFrame* firstInvalid = nullptr;
1214 		PageItem_TextFrame* prevInChain  = dynamic_cast<PageItem_TextFrame*>(m_backBox);
1215 		while (prevInChain)
1216 		{
1217 			if (prevInChain->invalid)
1218 				firstInvalid = prevInChain;
1219 			prevInChain = dynamic_cast<PageItem_TextFrame*>(prevInChain->m_backBox);
1220 		}
1221 		PageItem_TextFrame* nextInChain = firstInvalid;
1222 		while (nextInChain && (nextInChain != this))
1223 		{
1224 			nextInChain->layout();
1225 			nextInChain = dynamic_cast<PageItem_TextFrame*>(nextInChain->m_nextBox);
1226 		}
1227 		// #9592 : warning, BackBox->layout() may not layout BackBox next box
1228 		if (!invalid)
1229 			return;
1230 	}
1231 	else if (!invalid && OnMasterPage.isEmpty()) {
1232 //		qDebug() << QString("textframe: len=%1, invalid=%2 OnMasterPage=%3: no relayout").arg(itemText.length()).arg(invalid).arg(OnMasterPage);
1233 		return;
1234 	}
1235 	if (invalid && m_backBox == nullptr)
1236 		firstChar = 0;
1237 
1238 //	qDebug() << QString("textframe(%1,%2): len=%3, start relayout at %4").arg(m_xPos).arg(m_yPos).arg(itemText.length()).arg(firstInFrame());
1239 	QPoint pt1, pt2;
1240 	QRect pt;
1241 	double chs, chsd = 0;
1242 	double EndX, OFs;
1243 	ParagraphStyle style;
1244 	int opticalMargins = ParagraphStyle::OM_None;
1245 
1246 	bool outs = false;
1247 	bool goNoRoom = false;
1248 	bool goNextColumn = false;
1249 
1250 	TabControl tabs;
1251 	tabs.active    = false;     // RTab
1252 	tabs.status    = TabNONE;   // TabCode
1253 	tabs.tabGlyph  = nullptr;         // was int charIndex ~ StartRT
1254 	tabs.xPos      = 0;         // RTabX
1255 
1256 	QList<ParagraphStyle::TabRecord> tTabValues;
1257 	tTabValues.clear();
1258 
1259 	bool BulNumMode = false; //when bullet or counter should be inserted
1260 	bool   DropCmode = false, FlopBaseline = false;
1261 	double desc=0, asce=0, realAsce=0, realDesc = 0, offset = 0;
1262 	double maxDY=0, maxDX=0;
1263 	double DropCapDrop = 0;
1264 	int    DropLines = 0;
1265 	int    DropLinesCount = 0;
1266 
1267 	textLayout.clear();
1268 	incompleteLines = 0;
1269 	incompletePositions.clear();
1270 
1271 	double lineCorr = 0;
1272 	if (lineColor() != CommonStrings::None)
1273 		lineCorr = m_lineWidth / 2.0;
1274 
1275 	//hold Y position of last computed line of text (with glyphs descent)
1276 	//for moving next line if glyphs are higher than that
1277 	double lastLineY = 0;
1278 
1279 	QMap<int, Mark*> noteMarksPosMap;  //maping notes marks and its position in text
1280 
1281 	// dump styles
1282 /*
1283 	for (int i=0; i < itemText.nrOfParagraphs(); ++i) {
1284 		const ParagraphStyle& pstyle(itemText.paragraphStyle(itemText.endOfParagraph(i)));
1285 		qDebug("par %d:", i);
1286 		dumpIt(pstyle);
1287 	}
1288 	qDebug() << "default:";
1289 	dumpIt(itemText.defaultStyle());
1290 */
1291 
1292 	setShadow();
1293 	int itLen = itemText.length();
1294 	//fast validate empty frames
1295 	if (itLen == 0 || firstInFrame() == itLen)
1296 	{
1297 		PageItem_TextFrame * next = this;
1298 		while (next != nullptr)
1299 		{
1300 			next->invalid = false;
1301 			next->firstChar = itLen;
1302 			next->m_maxChars = itLen;
1303 			next->textLayout.clear();
1304 			next = dynamic_cast<PageItem_TextFrame*>(next->nextInChain());
1305 		}
1306 		// TODO layout() shouldn't delete any frame here, as it breaks any loop
1307 		// over the doc->Items or doc->MasterItems lists when done with indexes
1308 		// see Bug #12685 for an example
1309 		if (!isNoteFrame() && m_Doc->notesChanged() && !m_notesFramesMap.isEmpty())
1310 		{ //if notes are used
1311 			UndoManager::instance()->setUndoEnabled(false);
1312 			QList<PageItem_NoteFrame*> delList;
1313 			for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
1314 			{
1315 				if (nF->notesList().isEmpty() && !nF->isAutoNoteFrame())
1316 					delList.append(nF);
1317 			}
1318 			while (!delList.isEmpty())
1319 				m_Doc->delNoteFrame(delList.takeFirst(), false);
1320 			UndoManager::instance()->setUndoEnabled(true);
1321 		}
1322 		return;
1323 	}
1324 
1325 	if ((itLen != 0)) // || (NextBox != nullptr))
1326 	{
1327 		// determine layout area
1328 		m_availableRegion = calcAvailableRegion();
1329 		if (m_availableRegion.isEmpty())
1330 		{
1331 			m_maxChars = firstInFrame();
1332 			goto NoRoom;
1333 		}
1334 
1335 		if (imageFlippedH() || imageFlippedV())
1336 		{
1337 			QTransform matrix;
1338 			if (imageFlippedH())
1339 			{
1340 				matrix.translate(m_width, 0);
1341 				matrix.scale(-1, 1);
1342 			}
1343 			if (imageFlippedV())
1344 			{
1345 				matrix.translate(0, m_height);
1346 				matrix.scale(1, -1);
1347 			}
1348 			m_availableRegion = matrix.map(m_availableRegion);
1349 		}
1350 
1351 		// update Bullet & number list if any.
1352 		if (itemText.hasTextMarks() || itemText.hasBulletOrNum() ||  itemText.marksCountChanged())
1353 		{
1354 			updateBulletsNum();
1355 			itemText.resetMarksCountChanged();
1356 		}
1357 
1358 		ITextContext* context = this;
1359 		//TextShaper textShaper(this, itemText, firstInFrame());
1360 		ShapedTextFeed shapedText(&itemText, firstInFrame(), context);
1361 
1362 		QList<GlyphCluster> glyphClusters; // = textShaper.shape();
1363 		// std::sort(glyphClusters.begin(), glyphClusters.end(), logicalGlyphRunComp);
1364 
1365 		LineControl current(m_width, m_height, m_textDistanceMargins, lineCorr, m_Doc, context, columnWidth(), m_columnGap);
1366 		current.nextColumn(textLayout);
1367 
1368 		lastLineY = m_textDistanceMargins.top();
1369 
1370 		//automatic line spacing factor (calculated once)
1371 		double autoLS = static_cast<double>(context->typographicPrefs().autoLineSpacing) / 100.0;
1372 
1373 		// find start of first line
1374 		if (firstInFrame() < itLen)
1375 		{
1376 			const CharStyle& cstyle = itemText.charStyle(firstInFrame());
1377 			style = itemText.paragraphStyle(firstInFrame());
1378 			style.setLineSpacing (calculateLineSpacing (style, this));
1379 
1380 //			qDebug() << QString("style @0: %1 -- %2, %4/%5 char: %3").arg(style.leftMargin()).arg(style.rightMargin())
1381 //				   .arg(style.charStyle().asString()).arg(style.name()).arg(style.parent()?style.parent()->name():"");
1382 			if (style.hasDropCap())
1383 			{
1384 				chs = calculateLineSpacing (style, this) * style.dropCapLines() * 10;
1385 			}
1386 			else
1387 				chs = cstyle.fontSize();
1388 			desc = -cstyle.font().descent(chs / 10.0);
1389 			current.yPos = m_textDistanceMargins.top() + lineCorr;
1390 //			qDebug() << QString("first line at y=%1").arg(current.yPos);
1391 
1392 		}
1393 		else // empty itemText:
1394 		{
1395 			desc = -itemText.defaultStyle().charStyle().font().descent(itemText.defaultStyle().charStyle().fontSize() / 10.0);
1396 			current.yPos = itemText.defaultStyle().lineSpacing() + m_textDistanceMargins.top() + lineCorr - desc;
1397 		}
1398 
1399 		current.startLine(0);
1400 
1401 		outs = false;
1402 		OFs = 0;
1403 		m_maxChars = 0;
1404 		double realEnd = 0;
1405 		current.restartIndex = current.restartRowIndex = 0;
1406 		current.afterOverflow = false;
1407 		current.addLine = false;
1408 		current.recalculateY = true;
1409 		current.lastInRowLine = false;
1410 		current.addLeftIndent = true;
1411 		current.wasFirstInRow = false;
1412 		current.leftIndent = 0.0;
1413 		current.rightIndent = 0.0;
1414 		current.rightMargin = 0.0;
1415 		current.mustLineEnd = current.colRight;
1416 		current.restartX = 0;
1417 
1418 		//why emit invalidating signals each time text is changed by appling styles?
1419 		//this speed up layouting in case of using notes marks and drop caps
1420 		itemText.blockSignals(true);
1421 		setMaxY(-1);
1422 		double maxYAsc = 0.0, maxYDesc = 0.0;
1423 		int regionMinY = 0, regionMaxY= 0;
1424 
1425 		double autoLeftIndent = 0.0;
1426 		for (int i = 0; shapedText.haveMoreText(i, glyphClusters); ++i)
1427 		{
1428 			int currentIndex = i - current.lineData.firstCluster;
1429 			GlyphCluster newRun = glyphClusters[i];
1430 			if (currentIndex >= current.glyphs.count())
1431 				current.glyphs.append(newRun);
1432 			else
1433 				current.glyphs[currentIndex] = newRun;
1434 
1435 			if (current.glyphs[currentIndex].glyphs().isEmpty())
1436 				continue;
1437 
1438 			int a = current.glyphs[currentIndex].firstChar();
1439 			bool HasObject = itemText.hasObject(a);
1440 			PageItem* currentObject = itemText.object(a).getPageItem(m_Doc);
1441 			QRectF currentObjectBox = QRectF();
1442 			if (HasObject)
1443 				currentObjectBox = currentObject->getVisualBoundingRect();
1444 
1445 			bool HasMark = itemText.hasMark(a);
1446 
1447 			if (HasMark)
1448 			{
1449 				Mark* mark = itemText.mark(a);
1450 				//store mark pointer and position in text
1451 				if (mark->isType(MARKNoteMasterType))
1452 					noteMarksPosMap.insert(a, mark);
1453 			}
1454 
1455 			BulNumMode = false;
1456 			if (itemText.isBlockStart(a))
1457 			{
1458 				autoLeftIndent = 0.0;
1459 				style = itemText.paragraphStyle(a);
1460 				if (style.hasBullet() || style.hasNum())
1461 				{
1462 					BulNumMode = true;
1463 				}
1464 			}
1465 
1466 			if (current.isEmpty)
1467 				opticalMargins = style.opticalMargins();
1468 			//-->#13490
1469 			if (isNoteFrame() && !HasMark)
1470 			{
1471 				StyleFlag s(itemText.charStyle(a).effects());
1472 				s &= ~ScStyle_Superscript;
1473 				CharStyle haveSuperscript;
1474 				haveSuperscript.setFeatures(s.featureList());
1475 				itemText.applyCharStyle(a, 1, haveSuperscript);
1476 			}
1477 			//--<#13490
1478 			CharStyle charStyle = ((itemText.text(a) != SpecialChars::PARSEP) ? itemText.charStyle(a) : style.charStyle());
1479 
1480 			double hlcsize10 = charStyle.fontSize() / 10.0;
1481 			double scaleV = charStyle.scaleV() / 1000.0;
1482 			double scaleH = charStyle.scaleH() / 1000.0;
1483 			double offset = hlcsize10 * (charStyle.baselineOffset() / 1000.0);
1484 			style.setLineSpacing (calculateLineSpacing (style, this));
1485 			FlopBaseline = (current.startOfCol && firstLineOffset() == FLOPBaselineGrid);
1486 
1487 			// find out about par gap and dropcap
1488 			if (a == firstInFrame())
1489 			{
1490 				if (itemText.isBlockStart(a))
1491 				{
1492 					if (!itemText.isBlockStart(a + 1))
1493 					{
1494 						DropCmode = style.hasDropCap();
1495 						if (DropCmode)
1496 							DropLines = style.dropCapLines();
1497 					}
1498 					else
1499 						DropCmode = false;
1500 					current.lineData.isFirstLine = true;
1501 				}
1502 			}
1503 
1504 			const ScFace& font = charStyle.font();
1505 
1506 			current.glyphs[currentIndex].clearFlag(ScLayout_DropCap);
1507 			current.glyphs[currentIndex].clearFlag(ScLayout_SoftHyphenVisible);
1508 
1509 			// No space at begin of line,
1510 			if (legacy)
1511 			{
1512 				// unless at begin of par (eeks)
1513 				if ( (current.isEmpty) && (SpecialChars::isBreakingSpace(itemText.text(a)))
1514 					 && (a > 0 && ! SpecialChars::isBreak(itemText.text(a - 1)))
1515 					 && ! (a > 0 && SpecialChars::isBreakingSpace(itemText.text(a - 1))
1516 						   && (!glyphClusters[i - 1].hasFlag(ScLayout_SuppressSpace))))
1517 				{
1518 					current.glyphs[currentIndex].setFlag(ScLayout_SuppressSpace);
1519 					continue;
1520 				}
1521 				current.glyphs[currentIndex].clearFlag(ScLayout_SuppressSpace);
1522 			}
1523 			else // from 134 on use NBSPACE for this effect
1524 			{
1525 				if (current.isEmpty && (SpecialChars::isBreakingSpace(itemText.text(a)) || itemText.text(a).isSpace()))
1526 				{
1527 					current.glyphs[currentIndex].setFlag(ScLayout_SuppressSpace);
1528 					continue;
1529 				}
1530 				current.glyphs[currentIndex].clearFlag(ScLayout_SuppressSpace);
1531 			}
1532 			if (current.isEmpty)
1533 			{
1534 				if (style.rightMargin() == 0)
1535 				{
1536 					//addLine = true;
1537 					current.rightMargin = 0.0;
1538 				}
1539 				else
1540 				{
1541 					if (current.lastInRowLine)
1542 						current.rightMargin = style.rightMargin();
1543 					else
1544 						current.rightMargin = 0.0;
1545 				}
1546 				current.breakIndex = -1;
1547 				if (current.startOfCol && !current.afterOverflow && current.recalculateY)
1548 					current.yPos = qMax(current.yPos, m_textDistanceMargins.top());
1549 				// more about par gap and dropcaps
1550 				if ((a > firstInFrame() && itemText.isBlockStart(a)) || (a == 0 && m_backBox == nullptr && current.startOfCol))
1551 				{
1552 					if (!current.afterOverflow && current.recalculateY && !current.startOfCol)
1553 						current.yPos += style.gapBefore();
1554 					DropCapDrop = 0;
1555 					if (!itemText.isBlockStart(a + 1))
1556 						DropCmode = style.hasDropCap();
1557 					else
1558 						DropCmode = false;
1559 					if (DropCmode && !current.afterOverflow)
1560 					{
1561 						DropLines = style.dropCapLines();
1562 //						DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
1563 //						qDebug() << QString("dropcapdrop: y=%1+%2").arg(current.yPos).arg(DropCapDrop);
1564 					}
1565 					current.lineData.isFirstLine = true;
1566 				}
1567 			}
1568 			// find charsize factors
1569 			if (DropCmode)
1570 			{
1571 				DropCapDrop = calculateLineSpacing (style, this) * (DropLines - 1);
1572 
1573 				//text height, width, ascent and descent should be calculated for whole text provided by ScText in current position
1574 				//and that may be more than one char (variable text for example)
1575 				double realCharHeight = 0.0, realCharAscent = 0.0;
1576 				const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs();
1577 				for (const GlyphLayout& gl : glyphs) {
1578 					GlyphMetrics gm = font.glyphBBox(gl.glyph, style.charStyle().fontSize() / 10.0);
1579 					realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
1580 					realCharAscent = qMax(realCharAscent, gm.ascent);
1581 				}
1582 				if (realCharHeight == 0.0)
1583 					realCharHeight = font.height(style.charStyle().fontSize() / 10.0);
1584 				if (realCharAscent == 0.0)
1585 					realCharAscent = font.ascent(style.charStyle().fontSize() / 10.0);
1586 				if (current.startOfCol && (m_firstLineOffset == FLOPFontAscent))
1587 					realCharAscent = font.ascent(hlcsize10);
1588 				chsd = ((DropCapDrop + realCharAscent) / realCharHeight) * style.charStyle().fontSize();
1589 				chs  = ((DropCapDrop + realCharAscent) / realCharAscent) * style.charStyle().fontSize();
1590 				current.glyphs[currentIndex].setFlag(ScLayout_DropCap);
1591 				if (HasObject)
1592 				{
1593 				//	chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
1594 				//	chsd = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
1595 					chs = currentObjectBox.height() * 10;
1596 					chsd = currentObjectBox.height() * 10;
1597 				}
1598 			}
1599 			else // ! dropCapMode
1600 			{
1601 				if (HasObject)
1602 					chs = currentObjectBox.height() * 10;
1603 				//	chs = qRound((currentObject->height() + currentObject->lineWidth()) * 10);
1604 				else
1605 					chs = charStyle.fontSize();
1606 			}
1607 			// set StartOfLine
1608 			if (current.isEmpty)
1609 				current.glyphs[currentIndex].setFlag(ScLayout_StartOfLine);
1610 			else
1611 				current.glyphs[currentIndex].clearFlag(ScLayout_StartOfLine);
1612 
1613 			if (!current.glyphs[currentIndex].hasFlag(ScLayout_StartOfLine))
1614 			{
1615 				double tracking = charStyle.fontSize() * charStyle.tracking() / 10000.0;
1616 				current.glyphs[currentIndex].xoffset += tracking;
1617 				if (current.glyphs[currentIndex].width() != 0)
1618 					current.glyphs[currentIndex].extraWidth += tracking;
1619 			}
1620 
1621 //			glyphs->yadvance = 0;
1622 
1623 			// adjust space between CJK and Latin letter
1624 			if (!current.glyphs[currentIndex].hasFlag(ScLayout_StartOfLine) && current.glyphs[currentIndex].hasFlag(ScLayout_CJKLatinSpace))
1625 			{
1626 				double quaterEM = charStyle.fontSize() / 10 / 4;
1627 				current.glyphs[currentIndex].extraWidth += quaterEM;
1628 				current.glyphs[currentIndex].xoffset += quaterEM;
1629 			}
1630 
1631 			if (i == current.lineData.firstCluster && current.glyphs[currentIndex].hasFlag(ScLayout_CJKFence))
1632 			{
1633 				current.glyphs[currentIndex].extraWidth -= (charStyle.fontSize() / 10 / 2);
1634 				current.glyphs[currentIndex].xoffset -= (charStyle.fontSize() / 10 / 2);
1635 			}
1636 			// find out width, ascent and descent of char
1637 			double wide = current.glyphs[currentIndex].width();
1638 
1639 			if (DropCmode)
1640 			{
1641 				// drop caps are wider...
1642 				GlyphCluster& glyphCluster = current.glyphs[currentIndex];
1643 				if (HasObject)
1644 				{
1645 				//	double itemHeight = currentObject->height() + currentObject->lineWidth();
1646 				//	if (itemHeight == 0)
1647 				//		itemHeight = font.height(style.charStyle().fontSize() / 10.0);
1648 				//	asce = currentObject->height() + currentObject->lineWidth();
1649 				//	wide = currentObject->width() + currentObject->lineWidth();
1650 					double itemHeight = currentObjectBox.height();
1651 					if (itemHeight == 0)
1652 						itemHeight = font.height(style.charStyle().fontSize() / 10.0);
1653 					asce = currentObjectBox.height();
1654 					wide = currentObjectBox.width();
1655 					realAsce = calculateLineSpacing (style, this) * DropLines;
1656 					glyphCluster.setScaleH(glyphCluster.scaleH() / glyphCluster.scaleV());
1657 					glyphCluster.setScaleV(realAsce / itemHeight);
1658 					glyphCluster.setScaleH(glyphCluster.scaleH() * glyphCluster.scaleV());
1659 					wide *= glyphCluster.scaleH();
1660 				}
1661 				else
1662 				{
1663 					double realCharHeight = 0.0, realCharAscent = 0.0;
1664 					wide = 0.0; realAsce = 0.0;
1665 					const QList<GlyphLayout>& glyphs = glyphCluster.glyphs();
1666 					for (const GlyphLayout& gl : glyphs) {
1667 						GlyphMetrics gm = font.glyphBBox(gl.glyph, charStyle.fontSize() / 10.0);
1668 						realCharHeight = qMax(realCharHeight, gm.ascent + gm.descent);
1669 						realCharAscent = qMax(realCharAscent, gm.ascent);
1670 						gm = font.glyphBBox(gl.glyph, chsd / 10.0);
1671 						realAsce = qMax(realAsce, gm.ascent + gm.descent);
1672 						wide += gm.width;
1673 					}
1674 					wide = (wide * scaleH) + (1 - scaleH);
1675 					realAsce = realAsce  * scaleV + offset;
1676 					if (realCharHeight == 0)
1677 						realCharHeight = font.height(style.charStyle().fontSize() / 10.0);
1678 					if (realCharAscent == 0)
1679 						realCharAscent = font.ascent(style.charStyle().fontSize() / 10.0);
1680 					asce = realCharAscent;
1681 					if (current.startOfCol && (m_firstLineOffset == FLOPFontAscent))
1682 						asce = font.ascent(hlcsize10);
1683 					glyphCluster.setScaleH(glyphCluster.scaleH() / glyphCluster.scaleV());
1684 					glyphCluster.setScaleV(realAsce / realCharHeight);
1685 					glyphCluster.setScaleH(glyphCluster.scaleH() * glyphCluster.scaleV());
1686 					glyphCluster.xoffset -= 0.5; //drop caps are always to far from column left edge
1687 				}
1688 				// This is to mimic pre-boxes branches in case first character of paragraph is a space
1689 				// If we don't do this, paragraph offset will not apply correctly to first line
1690 				if ((glyphCluster.scaleH() == 0.0) || (glyphCluster.scaleV() == 0.0))
1691 				{
1692 					glyphCluster.setScaleH(1.0);
1693 					glyphCluster.setScaleV(1.0);
1694 					wide = 0.0;
1695 				}
1696 				desc = realDesc = 0;
1697 			}
1698 			else // !DropCMode
1699 			{
1700 				// find ascent / descent
1701 				if (HasObject)
1702 					desc = realDesc = 0;
1703 				else
1704 				{
1705 					if (itemText.text(a) != SpecialChars::OBJECT)
1706 					{
1707 						const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs();
1708 						for (const GlyphLayout& gl : glyphs)
1709 						{
1710 							GlyphMetrics gm = font.glyphBBox(gl.glyph, hlcsize10);
1711 							realDesc = qMax(realDesc, gm.descent * scaleV - offset);
1712 							realAsce = gm.ascent;
1713 						}
1714 					}
1715 					desc = -font.descent(hlcsize10);
1716 					current.rememberShrinkStretch(itemText.text(a), wide, style);
1717 				}
1718 				if (HasObject)
1719 				{
1720 				//	asce = currentObject->height() + currentObject->lineWidth();
1721 					asce = currentObjectBox.height();
1722 					realAsce = asce * scaleV + offset;
1723 					wide = currentObjectBox.width() * scaleH;
1724 				}
1725 				else
1726 				{
1727 					asce = font.ascent(hlcsize10);
1728 					if (HasMark && !BulNumMode)
1729 						realAsce = asce * scaleV + offset;
1730 					else
1731 					{
1732 						const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs();
1733 						for (const GlyphLayout& gl : glyphs)
1734 							realAsce = qMax(realAsce, font.glyphBBox(gl.glyph, hlcsize10).ascent * scaleV + offset);
1735 					}
1736 				}
1737 			}
1738 //			if (BulNumMode)
1739 //				hl->glyph.last()->xadvance += style.parEffectOffset();
1740 			//check for Y position at beginning of line
1741 			if (current.isEmpty && !current.afterOverflow)
1742 			{
1743 				if (current.recalculateY)
1744 				{
1745 					//if top of column Y position depends on first line offset
1746 					if (current.startOfCol)
1747 					{
1748 						lastLineY = qMax(lastLineY, m_textDistanceMargins.top() + lineCorr);
1749 						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
1750 						{
1751 							if (current.yPos <= lastLineY)
1752 								current.yPos = lastLineY + 1;
1753 							double by = m_yPos;
1754 							if (OwnPage != -1)
1755 								by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
1756 							int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1757 							int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1758 							current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1759 						}
1760 						else if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
1761 						{
1762 							if (firstLineOffset() == FLOPRealGlyphHeight)
1763 							{
1764 								if (DropCmode || (itemText.text(a) == SpecialChars::PARSEP))
1765 									current.yPos += asce;
1766 								else
1767 									current.yPos += realAsce;
1768 							}
1769 							else if (firstLineOffset() == FLOPLineSpacing)
1770 								current.yPos += style.lineSpacing();
1771 							else
1772 								current.yPos += asce;
1773 						}
1774 					}
1775 					else
1776 					{
1777 						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
1778 						{
1779 							current.yPos += m_Doc->guidesPrefs().valueBaselineGrid;
1780 							double by = m_yPos;
1781 							if (OwnPage != -1)
1782 								by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
1783 							int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1784 							int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1785 							current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1786 						}
1787 						else
1788 							current.yPos += style.lineSpacing();
1789 					}
1790 					if (DropCmode)
1791 						current.yPos += DropCapDrop;
1792 				}
1793 				//set left indentation
1794 				current.leftIndent = 0.0;
1795 				if (current.addLeftIndent && ((maxDX == 0) || DropCmode || BulNumMode))
1796 				{
1797 					current.leftIndent = style.leftMargin() + autoLeftIndent;
1798 					if (itemText.isBlockStart(a))
1799 					{
1800 						if (style.direction() == ParagraphStyle::RTL)
1801 						{
1802 							// use rightIndent to not mess with old behavior
1803 							current.rightIndent = style.firstIndent();
1804 							// line width should consider RTL indent when it breaks the line.
1805 							current.mustLineEnd = current.colRight - current.rightIndent;
1806 						}
1807 						else
1808 							current.leftIndent += style.firstIndent();
1809 						if (BulNumMode || DropCmode)
1810 						{
1811 							if (style.parEffectIndent())
1812 							{
1813 								current.leftIndent -= style.parEffectOffset() + wide;
1814 								if (current.leftIndent < 0.0)
1815 								{
1816 									autoLeftIndent = abs(current.leftIndent);
1817 									current.leftIndent = 0.0;
1818 								}
1819 							}
1820 						}
1821 					}
1822 					current.addLeftIndent = false;
1823 				}
1824 			}
1825 			current.recalculateY = true;
1826 			maxYAsc = maxYDesc = 0.0;
1827 			double addAsce = 0.0;
1828 			if (current.startOfCol)
1829 			{
1830 				if (DropCmode)
1831 					addAsce = qMax(realAsce, asce + offset);
1832 				else
1833 					addAsce = asce + offset;
1834 				if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
1835 				{
1836 					if (firstLineOffset() == FLOPRealGlyphHeight)
1837 						addAsce = realAsce;
1838 					else if (firstLineOffset() == FLOPLineSpacing)
1839 					{
1840 						if (DropCmode)
1841 							addAsce = DropCapDrop + style.lineSpacing();
1842 						else
1843 							addAsce = style.lineSpacing();
1844 					}
1845 				}
1846 				maxYAsc = current.yPos - addAsce;
1847 			}
1848 			else
1849 				maxYAsc = current.yPos - realAsce;
1850 			//fix for glyphs with negative realAsce value
1851 			maxYAsc = qMax(maxYAsc, 0.0);
1852 			maxYDesc = current.yPos + realDesc;
1853 			if (style.lineSpacingMode() != ParagraphStyle::AutomaticLineSpacing)
1854 			{
1855 				// #11727, #11628, etc.
1856 				maxYAsc = qMax(0.0, qMin(maxYAsc, current.yPos - asce));
1857 				maxYDesc = current.yPos + desc;
1858 			}
1859 
1860 			regionMinY = static_cast<int>(floor(maxYAsc));
1861 			regionMaxY = static_cast<int>(floor(maxYDesc));
1862 
1863 			if (current.isEmpty && !current.afterOverflow)
1864 			{
1865 				//start a new line
1866 				goNoRoom = false;
1867 
1868 				// find line`s start
1869 				pt1 = QPoint(static_cast<int>(floor(current.xPos)), regionMinY);
1870 				pt2 = QPoint(static_cast<int>(floor(current.xPos + (style.minGlyphExtension() * wide))), regionMaxY - 1);
1871 				pt = QRect(pt1, pt2);
1872 				realEnd = 0;
1873 				//check if there is overflow at start of line, if so jump behind it and check again
1874 				double Xpos, Xend;
1875 				bool done = false;
1876 				bool newColumn = false;
1877 
1878 				//FIX ME - that should be paragraph style`s properties
1879 				//if set then indent is add to possible line start point (after overflow)
1880 				//if not then indent is calculated from column left edge
1881 				//if you don't agree that adding indent to overflow should be default behaviour
1882 				//then change it to false
1883 				bool addIndent2overflow = false; // should be addIndent2Overflow = style.addIndent2Overlow();
1884 				bool addFirstIndent2overflow = true; // should be addFirstIndent2Overflow = style.addFirstIndent2Overlow();
1885 				//if first line indent is negative and left indent should not be added to overflow
1886 				//then don't add first line ident either
1887 				if ((style.firstIndent() < 0) && !addIndent2overflow)
1888 					addFirstIndent2overflow = false;
1889 
1890 				while (!done)
1891 				{
1892 					Xpos = current.xPos + (addIndent2overflow ? 0 : current.leftIndent);
1893 					Xend = current.xPos + current.leftIndent;
1894 					//check if in indent any overflow occurs
1895 					while (Xpos <= Xend && Xpos < current.colRight)
1896 					{
1897 						pt.moveTopLeft(QPoint(static_cast<int>(floor(Xpos)), regionMinY));
1898 						if (!regionContainsRect(m_availableRegion, pt))
1899 						{
1900 							Xpos = current.xPos = realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
1901 							Xend = current.xPos + (addIndent2overflow ? current.leftIndent : 0);
1902 							//for first paragraph`s line - if first line offset should be added
1903 							if ( addFirstIndent2overflow && itemText.isBlockStart(a))
1904 								Xend += style.firstIndent();
1905 						}
1906 						else
1907 							Xpos++;
1908 					}
1909 					current.xPos = Xend;
1910 					done = true;
1911 					if (current.isEndOfLine((style.minGlyphExtension() * wide) + current.rightMargin))
1912 					{
1913 						// new line
1914 						current.xPos = qMax(current.colLeft, maxDX);
1915 						if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
1916 						{
1917 							current.yPos++;
1918 							double by = m_yPos;
1919 							if (OwnPage != -1)
1920 								by = m_yPos - m_Doc->Pages->at(OwnPage)->yOffset();
1921 							int ol1 = qRound((by + current.yPos - m_Doc->guidesPrefs().offsetBaselineGrid) * 10000.0);
1922 							int ol2 = static_cast<int>(ol1 / m_Doc->guidesPrefs().valueBaselineGrid);
1923 							current.yPos = ceil(  ol2 / 10000.0 ) * m_Doc->guidesPrefs().valueBaselineGrid + m_Doc->guidesPrefs().offsetBaselineGrid - by;
1924 						}
1925 						else if (style.lineSpacingMode() == ParagraphStyle::FixedLineSpacing)
1926 							current.yPos += (current.startOfCol ? 1 : style.lineSpacing());
1927 						else
1928 							current.yPos += (current.startOfCol ? 1 : style.lineSpacing());
1929 						lastLineY = maxYAsc;
1930 
1931 						if (current.startOfCol)
1932 							maxYAsc = current.yPos - addAsce;
1933 						else
1934 							maxYAsc = current.yPos - realAsce;
1935 						maxYAsc = qMax(maxYAsc, 0.0);
1936 						maxYDesc = current.yPos + realDesc;
1937 						if (style.lineSpacingMode() != ParagraphStyle::AutomaticLineSpacing)
1938 						{
1939 							// #11727, #11628, etc.
1940 							maxYAsc = qMax(0.0, qMin(maxYAsc, current.yPos - asce));
1941 							maxYDesc = current.yPos + desc;
1942 						}
1943 
1944 						regionMinY = static_cast<int>(floor(maxYAsc));
1945 						regionMaxY = static_cast<int>(floor(maxYDesc));
1946 
1947 						pt.moveTopLeft(QPoint(static_cast<int>(floor(current.xPos)), regionMinY));
1948 						done = false;
1949 					}
1950 					if (current.isEndOfCol(realDesc))
1951 					{
1952 						current.column++;
1953 						if (current.column < m_columns)
1954 						{
1955 							newColumn = true;
1956 							break;
1957 						}
1958 						m_maxChars = a;
1959 						goto NoRoom;
1960 					}
1961 				}
1962 				if (newColumn)
1963 				{
1964 					current.nextColumn(textLayout);
1965 					current.mustLineEnd = current.colRight;
1966 					current.restartX = current.xPos;
1967 					lastLineY = current.yPos;
1968 					current.rowDesc = 0;
1969 					i--;
1970 					current.recalculateY = true;
1971 					current.addLeftIndent = true;
1972 					continue;
1973 				}
1974 				current.lineData.x = current.restartX = current.xPos;
1975 				current.lineData.y = current.yPos;
1976 //				if (glyphClusters[current.line.firstCluster].hasFlag(ScLayout_DropCap))
1977 //					current.line.y -= DropCapDrop;
1978 			}
1979 
1980 			//check if line must start at new Y position due to current glyph height or previous line descent
1981 			if (!SpecialChars::isBreak(itemText.text(a), true)
1982 				&& !SpecialChars::isBreakingSpace(itemText.text(a))
1983 				&& !SpecialChars::isExpandingSpace(itemText.text(a))
1984 				&& itemText.text(a) != SpecialChars::TAB)
1985 			{
1986 				double diff = 0;
1987 				if (current.startOfCol || DropCmode)
1988 					diff = realAsce - (current.yPos - lastLineY);
1989 				else if (style.lineSpacingMode() != ParagraphStyle::FixedLineSpacing)
1990 				{
1991 					if (HasObject)
1992 					//	diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY);
1993 						diff = (currentObjectBox.height() * scaleV + offset) - (current.yPos - lastLineY);
1994 					else
1995 					{
1996 					//	Pre-boxes code equivalent below:
1997 					//	int glyphID = font.char2CMap(QChar('l'));
1998 					//	diff = font.glyphBBox(glyphID, hlcsize10).ascent * scaleV + offset - (current.yPos - lastLineY);
1999 						diff = realAsce - (current.yPos - lastLineY);
2000 					}
2001 				}
2002 				else
2003 				{
2004 					if (HasObject)
2005 					//	diff = (currentObject->height() + currentObject->lineWidth()) * scaleV + offset - (current.yPos - lastLineY);
2006 						diff = currentObjectBox.height() * scaleV + offset - (current.yPos - lastLineY);
2007 				}
2008 				if (diff >= 1 || (!DropCmode && diff > 0))
2009 				{
2010 					if (current.hasDropCap && DropLinesCount == 0)
2011 					{
2012 						current.hasDropCap = false;
2013 						current.yPos = maxDY;
2014 						maxDX = 0;
2015 					}
2016 					int linesDrop = 0;
2017 					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing || FlopBaseline)
2018 					{
2019 						linesDrop = ceil(diff / m_Doc->guidesPrefs().valueBaselineGrid);
2020 						current.yPos += m_Doc->guidesPrefs().valueBaselineGrid * linesDrop;
2021 						maxDX = 0;
2022 					}
2023 					else /*if (current.startOfCol)*/
2024 					{
2025 						//FIX ME - that is ugly hack I must made, because simply expression
2026 						//current.yPos += diff; stop working, don't know why (compiler bug?)
2027 						float YPOS = (float) current.yPos + (float) diff + 0.01;
2028 						current.yPos = (double) YPOS;
2029 						if (current.hasDropCap && diff > DropCapDrop)
2030 						{
2031 							current.hasDropCap = false;
2032 						}
2033 					}
2034 					if (current.hasDropCap && linesDrop > 0 && DropLinesCount > 1)
2035 					{
2036 						DropLinesCount += linesDrop;
2037 						if (DropLinesCount >= DropLines)
2038 						{
2039 							current.hasDropCap = false;
2040 							maxDX = 0;
2041 							maxDY = 0;
2042 						}
2043 					}
2044 					i = current.restartRow(false);
2045 					tabs.active = false;
2046 					tabs.status = TabNONE;
2047 					tabs.xPos   = 0.0;
2048 					continue;
2049 				}
2050 			}
2051 			// right tab stuff
2052 			if (tabs.active)
2053 			{
2054 				if (((itemText.text(a) == '.') && (tabs.status == TabPOINT)) || ((itemText.text(a) == ',') && (tabs.status == TabCOMMA)) || (itemText.text(a) == SpecialChars::TAB))
2055 				{
2056 					tabs.active = false;
2057 					tabs.status = TabNONE;
2058 				}
2059 			}
2060 			// tab positioning
2061 			if (itemText.text(a) == SpecialChars::TAB)
2062 			{
2063 				wide = 1;
2064 				if (tabs.active)
2065 					tabs.active = false;
2066 				else // ???
2067 				{
2068 					tabs.xPos = current.xPos;
2069 					tTabValues = style.tabValues();
2070 					if (tTabValues.isEmpty())
2071 					{
2072 						current.xPos = nextAutoTab (current, this);
2073 						tabs.status = TabNONE;
2074 						tabs.active = false;
2075 					}
2076 					else
2077 					{
2078 						double tCurX = current.xPos;
2079 						double oCurX = current.xPos - current.colLeft + wide;
2080 						for (int yg = static_cast<int>(tTabValues.count() - 1); yg > -1; yg--)
2081 						{
2082 							if (oCurX < tTabValues.at(yg).tabPosition)
2083 							{
2084 								tabs.status = static_cast<int>(tTabValues.at(yg).tabType);
2085 								tabs.fillChar = tTabValues.at(yg).tabFillChar;
2086 								current.xPos = current.colLeft + tTabValues.at(yg).tabPosition;
2087 							}
2088 						}
2089 						tabs.active = (tabs.status != TabLEFT);
2090 						if (current.xPos == tCurX)  // no more tabs found
2091 						{
2092 							current.xPos = nextAutoTab (current, this);
2093 							tabs.status = TabNONE;
2094 							tabs.active = false;
2095 							tabs.fillChar = QChar();
2096 						}
2097 
2098 						// remember fill char
2099 						if (!tabs.fillChar.isNull())
2100 						{
2101 							current.glyphs[currentIndex].setFlag(ScLayout_TabLeaders);
2102 							GlyphLayout tglyph;
2103 							tglyph.glyph	= font.char2CMap(tabs.fillChar.unicode());
2104 							tglyph.scaleV   = tglyph.scaleH = chs / charStyle.fontSize();
2105 							tglyph.xadvance = 0;
2106 							current.glyphs[currentIndex].append(tglyph);
2107 						}
2108 					}
2109 					current.xPos -= (legacy ? 1.0 : 0.0);
2110 					GlyphLayout& firstGlyph = current.glyphs[currentIndex].glyphs().first();
2111 					firstGlyph.xadvance = (current.xPos + wide - tabs.xPos) / firstGlyph.scaleH;
2112 					tabs.tabGlyph = &firstGlyph;
2113 				}
2114 			}
2115 
2116 			// remember y pos
2117 			if (DropCmode)
2118 			{
2119 				double yoffset = 0.0;
2120 				const QList<GlyphLayout>& glyphs = current.glyphs[currentIndex].glyphs();
2121 				for (const GlyphLayout& gl : glyphs)
2122 					yoffset = qMax(yoffset, font.glyphBBox(gl.glyph, chsd / 10.0).descent);
2123 				current.glyphs[currentIndex].yoffset -= yoffset;
2124 			}
2125 			// remember x pos
2126 			double breakPos = current.xPos;
2127 
2128 			if (!tabs.active) // normal case
2129 			{
2130 				current.xPos += wide;
2131 			}
2132 			else if (tabs.active && tabs.status == TabCENTER) 	// center tab
2133 			{
2134 				current.xPos += wide / 2;
2135 				current.xPos = qMax(current.xPos, current.colLeft);
2136 			}
2137 			else // other tabs.active
2138 			{
2139 				current.xPos = qMax(current.xPos, current.colLeft);
2140 			}
2141 			// remember possible break
2142 			if (shapedText.haveMoreText(i + 1, glyphClusters))
2143 			{
2144 				const GlyphCluster& nextCluster = glyphClusters[i + 1];
2145 				if (nextCluster.hasFlag(ScLayout_LineBoundary))
2146 				{
2147 					// #16100: why preventing possible line break when there are two
2148 					// consecutive line break opportunities? This is bad for CJK. /
2149 					if (/*!current.glyphs[currentIndex].hasFlag(ScLayout_LineBoundary)
2150 						&&*/ !current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible)
2151 						&& (itemText.text(a) != '-')
2152 						&& (itemText.text(a) != SpecialChars::SHYPHEN))
2153 					{
2154 						current.rememberBreak(i, breakPos, style.rightMargin());
2155 					}
2156 				}
2157 			}
2158 			if (HasObject)
2159 			{
2160 //				qDebug() << "rememberBreak object @" << i;
2161 				current.rememberBreak(i, breakPos, style.rightMargin());
2162 			}
2163 
2164 			//check against space before PARSEP
2165 			/*if (SpecialChars::isBreakingSpace(hl->ch) && (a + 1 < itemText.length()) && (itemText.item(a + 1)->ch == SpecialChars::PARSEP))
2166 			{
2167 				a++;
2168 				hl = itemText.item(a);
2169 			}*/
2170 
2171 			// test if end of line reached
2172 			double overflowWidth = 0.0;
2173 			double hyphWidth = 0.0;
2174 			bool inOverflow = false;
2175 			if (current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
2176 				hyphWidth = font.hyphenWidth(charStyle, hlcsize10) * (charStyle.scaleH() / 1000.0);
2177 			if ((current.isEndOfLine(style.rightMargin() + hyphWidth)) || current.isEndOfCol(realDesc) || SpecialChars::isBreak(itemText.text(a), m_columns > 1) || (current.xPos - current.maxShrink + hyphWidth) >= current.mustLineEnd)
2178 			{
2179 				//end of row reached - right column, end of column, break char or line must end
2180 				if (current.isEmpty && !current.afterOverflow && !SpecialChars::isBreak(itemText.text(a), m_columns > 1))
2181 				{
2182 					//no glyphs in line, so start new row
2183 					if (SpecialChars::isBreak(itemText.text(a), m_columns > 1))
2184 						current.restartRowIndex = i + 1;
2185 					i = current.restartRow(true);
2186 					inOverflow = false;
2187 					outs = false;
2188 					continue;
2189 				}
2190 				//there we have some glyphs
2191 				if (current.breakIndex < 0)
2192 				{
2193 					//force break
2194 					if (!SpecialChars::isBreak(itemText.text(a), m_columns > 1))
2195 					{
2196 						//force line end at previouse glyph
2197 						i--;
2198 						currentIndex = i - current.lineData.firstCluster;
2199 						a = (i >= 0) ? glyphClusters.at(i).firstChar() : (a - 1);
2200 						current.mustLineEnd = current.lineData.x;
2201 					}
2202 //					qDebug() << "breakline forced @" << i;
2203 					current.breakLine(i);
2204 				}
2205 				if (!current.addLine && !current.lastInRowLine && current.afterOverflow)
2206 				{
2207 					//we reach right edge of column
2208 					//if we are after overflow area we have to go back and try to insert text before it
2209 					//if we have some text here - insert text WITHOUT right margin
2210 					//if there is no place for text - insert text WITH right margin and end line
2211 					current.lastInRowLine = false;
2212 					if (current.lineData.firstCluster == current.restartIndex)
2213 						current.lastInRowLine = true;
2214 					if (current.hasDropCap && DropLinesCount == 0 && current.restartIndex == current.restartRowIndex)
2215 					{
2216 						current.hasDropCap = false;
2217 						maxDX = 0;
2218 						current.yPos = maxDY;
2219 					}
2220 					i = current.restartLine(false,true);
2221 					outs = false;
2222 					inOverflow = false;
2223 					continue;
2224 				}
2225 				outs = true;
2226 
2227 				current.addLine = true;
2228 				current.lastInRowLine = true;
2229 				current.rightMargin = style.rightMargin();
2230 				if (current.isEndOfCol(realDesc))
2231 					goNextColumn = true;
2232 			}
2233 			else
2234 			{
2235 				int charStart, charEnd;
2236 				if (current.isEmpty)
2237 				{
2238 					charStart = static_cast<int>(floor(current.lineData.x));
2239 					charEnd = static_cast<int>(ceil(current.xPos));
2240 				}
2241 				else
2242 				{
2243 					charStart = static_cast<int>(qMax(floor(current.xPos - current.maxShrink - (style.minGlyphExtension() * wide)),0.0));
2244 					charEnd = static_cast<int>(ceil(current.xPos - current.maxShrink));
2245 				}
2246 				if (legacy &&
2247 						(((itemText.text(a) == '-' || current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible)) && (current.hyphenCount < style.hyphenConsecutiveLines() || style.hyphenConsecutiveLines() == 0))
2248 						 || itemText.text(a) == SpecialChars::SHYPHEN))
2249 				{
2250 					if (current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
2251 					{
2252 						pt1 = QPoint(charStart,  regionMinY);
2253 						pt2 = QPoint(static_cast<int>(charEnd + hyphWidth), regionMaxY - 1);
2254 					}
2255 					else
2256 					{
2257 						pt1 = QPoint(charStart, regionMinY);
2258 						pt2 = QPoint(charEnd, regionMaxY - 1);
2259 					}
2260 				}
2261 				else if (!legacy && SpecialChars::isBreakingSpace(itemText.text(a)))
2262 				{
2263 					pt1 = QPoint(static_cast<int>(qMax(floor(breakPos - current.maxShrink - (style.minGlyphExtension() * wide)),0.0)), regionMinY);
2264 					pt2 = QPoint(charEnd, regionMaxY - 1);
2265 				}
2266 				else
2267 				{
2268 					pt1 = QPoint(charStart, regionMinY);
2269 					pt2 = QPoint(charEnd, regionMaxY - 1);
2270 				}
2271 				pt = QRect(pt1, pt2);
2272 				if (!regionContainsRect(m_availableRegion, pt))
2273 				{
2274 					realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
2275 					outs = true;
2276 				}
2277 				else if (style.rightMargin() > 0.0)
2278 				{
2279 					if (current.lastInRowLine || current.xPos - current.maxShrink + style.rightMargin() > current.colRight - style.rightMargin())
2280 						//condition after || is for find overflows in right margin area
2281 					{
2282 						pt.translate(static_cast<int>(ceil(style.rightMargin())), 0);
2283 						if (!regionContainsRect(m_availableRegion, pt))
2284 						{
2285 							realEnd = findRealOverflowEnd(m_availableRegion, pt, current.colRight);
2286 							outs = true;
2287 						}
2288 					}
2289 				}
2290 				if (outs)
2291 				{
2292 					if (current.isEmpty)
2293 					{
2294 						current.lineData.x = current.xPos = realEnd;
2295 						i--;
2296 						current.startLine(i + 1);
2297 						if (!current.wasFirstInRow)
2298 							current.addLeftIndent = true;
2299 						if (current.hasDropCap && DropLinesCount == 0 && !current.afterOverflow)
2300 						{
2301 							current.hasDropCap = false;
2302 							current.yPos = maxDY;
2303 						}
2304 						current.recalculateY = false;
2305 						outs = false;
2306 						continue;
2307 					}
2308 					if (current.breakIndex < 0)
2309 					{
2310 						i--;
2311 //						qDebug() << "forced break at glyph" << i;
2312 						currentIndex = i - current.lineData.firstCluster;
2313 						a = current.glyphs[currentIndex].firstChar();
2314 						current.breakLine(i);
2315 					}
2316 					//check if after overflow text can be placed
2317 					overflowWidth = realEnd - (current.xPos - current.maxShrink);
2318 					double newXAdd = overflowWidth - style.rightMargin() + style.minGlyphExtension() * wide + hyphWidth;
2319 					if (current.isEndOfLine(newXAdd) || current.xPos + newXAdd >= current.colRight || realEnd >= current.mustLineEnd)
2320 					{
2321 						if (!current.afterOverflow)
2322 						{
2323 							current.addLine = true;
2324 							current.lastInRowLine = true;
2325 						}
2326 						//line must end before overflov
2327 						if (!current.addLine && !current.lastInRowLine)
2328 						{
2329 							if (current.afterOverflow)
2330 							{
2331 								if (current.breakIndex < 0)
2332 								{
2333 									current.lastInRowLine = true;
2334 									current.mustLineEnd = current.lineData.x;
2335 								}
2336 								else if (current.lineData.firstCluster == current.restartIndex)
2337 									current.lastInRowLine = true;
2338 							}
2339 							else
2340 								current.lastInRowLine = true;
2341 							if (current.hasDropCap && DropLinesCount == 0)
2342 							{
2343 								current.hasDropCap = false;
2344 								maxDX = 0;
2345 							}
2346 							i = current.restartLine(false, true);
2347 							inOverflow = false;
2348 							outs = false;
2349 							continue;
2350 						}
2351 						current.addLine = true;
2352 						current.rightMargin = style.rightMargin();
2353 						current.lastInRowLine = true;
2354 					}
2355 					else
2356 						inOverflow = true;
2357 				}
2358 				else
2359 					setMaxY(maxYDesc);
2360 			}
2361 
2362 			// hyphenation
2363 			if ((current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible)
2364 				  || itemText.text(a) == '-'
2365 				  || itemText.text(a) == SpecialChars::SHYPHEN)
2366 				 && (!outs) && ((i == 0) || !itemText.text(glyphClusters[i - 1].lastChar()).isSpace()) )
2367 			{
2368 				breakPos = current.xPos;
2369 				if (itemText.text(a) != '-')
2370 				{
2371 					breakPos += hyphWidth;
2372 				}
2373 
2374 				double rightHang = 0;
2375 				if (opticalMargins & ParagraphStyle::OM_RightHangingPunct) {
2376 					rightHang = 0.7 * hyphWidth;
2377 				}
2378 
2379 				if (legacy || (breakPos - rightHang < current.colRight - style.rightMargin()))
2380 				{
2381 					if ((current.hyphenCount < style.hyphenConsecutiveLines()) || (style.hyphenConsecutiveLines() == 0) || itemText.text(a) == SpecialChars::SHYPHEN)
2382 					{
2383 //						qDebug() << "rememberBreak hyphen @" << i;
2384 						current.rememberBreak(i, breakPos, style.rightMargin() + hyphWidth);
2385 					}
2386 				}
2387 			}
2388 
2389 			if ((itemText.text(a) == SpecialChars::FRAMEBREAK) && (a < itemText.length() - 1))
2390 				goNoRoom = true;
2391 			if ((itemText.text(a) == SpecialChars::COLBREAK) && (m_columns > 1))
2392 				goNextColumn = true;
2393 
2394 			current.isEmpty = (i - current.lineData.firstCluster + 1) == 0;
2395 
2396 			if (tabs.active)
2397 			{
2398 				double cen = 1;
2399 				if (tabs.status == TabCENTER)
2400 					cen = 2;
2401 
2402 				double newTabAdvance = tabs.tabGlyph->xadvance;
2403 				newTabAdvance -= wide / cen / tabs.tabGlyph->scaleH;
2404 
2405 				if (newTabAdvance >= 0) {
2406 					tabs.tabGlyph->xadvance = newTabAdvance;
2407 				}
2408 				else {
2409 					tabs.active = false;
2410 					tabs.status = TabNONE;
2411 				}
2412 			}
2413 			if ((DropCmode || BulNumMode) && !outs)
2414 			{
2415 				current.xPos += style.parEffectOffset();
2416 				if (style.hasNum())
2417 				{
2418 					// make sure that Offset inserted only after the suffix
2419 					// loop over previous current.glyphs and set their extraWidth to 0.0
2420 					for (int j = 0; j < current.glyphs.size(); j++)
2421 					{
2422 						if (j != currentIndex)
2423 						{
2424 							GlyphCluster& currentGlyph = current.glyphs[j];
2425 							current.xPos -= currentGlyph.extraWidth;
2426 							currentGlyph.extraWidth = 0.0;
2427 						}
2428 					}
2429 				}
2430 				// set the offset for Drop Cap, Bullet & Number List
2431 				current.glyphs[currentIndex].extraWidth += style.parEffectOffset();
2432 
2433 				if (DropCmode)
2434 				{
2435 					DropCmode = false;
2436 					DropLinesCount = 0;
2437 					maxDY = current.yPos;
2438 					current.hasDropCap = true;
2439 					maxDX = current.xPos;
2440 					double spacing = calculateLineSpacing (style, this);
2441 					current.yPos -= spacing * (DropLines - 1);
2442 					if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
2443 						current.yPos = adjustToBaselineGrid (current, this, OwnPage);
2444 					current.recalculateY = false;
2445 				}
2446 			}
2447 			// end of line
2448 			if (outs)
2449 			{
2450 				tabs.active = false;
2451 				tabs.status = TabNONE;
2452 				tabs.xPos   = 0.0;
2453 				if (SpecialChars::isBreak(itemText.text(a), m_columns > 1))
2454 				{
2455 					// find end of line
2456 //					qDebug() << "breakline isBreak @" << i;
2457 					current.breakLine(i);
2458 					regionMinY = qMax(0.0, current.lineData.y - current.lineData.ascent);
2459 					regionMaxY = current.lineData.y + current.lineData.descent;
2460 					EndX = current.endOfLine(m_availableRegion, style.rightMargin(), regionMinY, regionMaxY);
2461 					current.finishLine(EndX - current.rightIndent);
2462 					//addLine = true;
2463 					assert(current.addLine);
2464 					//current.startOfCol = false;
2465 					//addLeftIndent = true;
2466 					if (itemText.isBlockStart(a + 1))
2467 					{
2468 						maxDX = 0;
2469 						if (current.hasDropCap)
2470 						{
2471 							current.yPos = maxDY;
2472 							maxDY = 0;
2473 							current.hasDropCap = false;
2474 						}
2475 					}
2476 
2477 //					if (style.alignment() != 0)
2478 					{
2479 						if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2480 							current.lineData.width += current.opticalRightMargin(itemText);
2481 
2482 						OFs = 0;
2483 						if (style.alignment() == ParagraphStyle::RightAligned)
2484 							OFs = current.lineData.width - current.lineData.naturalWidth;
2485 						if (style.alignment() == ParagraphStyle::Centered)
2486 							OFs = (current.lineData.width - current.lineData.naturalWidth) / 2;
2487 						if (style.alignment() == ParagraphStyle::Justified ||
2488 							style.alignment() == ParagraphStyle::Extended)
2489 						{
2490 							if (style.direction() == ParagraphStyle::RTL)
2491 								OFs = current.lineData.width - current.lineData.naturalWidth;
2492 							else
2493 								OFs = 0;
2494 						}
2495 
2496 						if (style.alignment() == ParagraphStyle::Extended
2497 							|| (style.alignment() == ParagraphStyle::Justified
2498 								&&  (itemText.text(a) == SpecialChars::LINEBREAK ||
2499 									 itemText.text(a) == SpecialChars::FRAMEBREAK ||
2500 									 itemText.text(a) == SpecialChars::COLBREAK)
2501 								&&  (current.lineData.lastCluster - 1 >= 0)
2502 								&&  !itemText.text(glyphClusters[current.lineData.lastCluster - 1].lastChar()).isSpace()))
2503 						{
2504 							current.justifyLine(style);
2505 						}
2506 						else
2507 						{
2508 							if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2509 								current.lineData.naturalWidth += current.opticalRightMargin(itemText);
2510 							double optiWidth = current.colRight - style.rightMargin() - style.lineSpacing()/2.0 - current.lineData.x;
2511 							if (current.lineData.naturalWidth > optiWidth)
2512 								current.lineData.width = qMax(current.lineData.width - current.maxShrink, optiWidth);
2513 							// simple offset
2514 							current.indentLine(style, OFs);
2515 						}
2516 						current.xPos = current.colRight;
2517 					}
2518 				}
2519 				else // outs -- last char went outside the columns (or into flow-around shape)
2520 				{
2521 					if (current.breakIndex >= 0)
2522 					{
2523 						// go back to last break position
2524 						i = current.breakIndex;
2525 						a = glyphClusters.at(i).firstChar();
2526 						currentIndex = i - current.lineData.firstCluster;
2527 						style = itemText.paragraphStyle(a);
2528 						const_cast<ScFace&>(font) = itemText.charStyle(a).font();
2529 						if (style.lineSpacingMode() == ParagraphStyle::AutomaticLineSpacing)
2530 							style.setLineSpacing(font.height(hlcsize10) * autoLS);
2531 						else if (style.lineSpacingMode() == ParagraphStyle::BaselineGridLineSpacing)
2532 							style.setLineSpacing(m_Doc->guidesPrefs().valueBaselineGrid);
2533 						charStyle = itemText.charStyle(a);
2534 					}
2535 					assert( i >= 0 );
2536 					assert( i < glyphClusters.length() );
2537 					//glyphs = itemText.getGlyphs(a);
2538 					current.isEmpty = (i - current.lineData.firstCluster + 1) == 0;
2539 					if (current.addLine && !current.isEmpty)
2540 					{
2541 						int supSpace = currentIndex;
2542 						while (supSpace > 0)
2543 						{
2544 							GlyphCluster& cluster = current.glyphs[supSpace];
2545 							if (cluster.glyphs().count() != 1 || !itemText.text(cluster.firstChar()).isSpace())
2546 								break;
2547 							cluster.setFlag(ScLayout_SuppressSpace);
2548 							--supSpace;
2549 						}
2550 
2551 						current.updateHeightMetrics();
2552 						//current.updateLineOffset(itemText, style, firstLineOffset());
2553 						//current.xPos = current.breakXPos;
2554 						EndX = current.endOfLine(m_availableRegion, current.rightMargin, regionMinY, regionMaxY);
2555 						current.finishLine(EndX - current.rightIndent);
2556 
2557 						hyphWidth = 0.0;
2558 						if (current.glyphs[currentIndex].hasFlag(ScLayout_HyphenationPossible) || itemText.text(a) == SpecialChars::SHYPHEN)
2559 						{
2560 							// insert hyphen
2561 							if (current.lastInRowLine)
2562 								//increase hyphen count only for hyphens a the end of text row, omit hyphens before overflow
2563 								current.hyphenCount++;
2564 							current.glyphs[currentIndex].setFlag(ScLayout_SoftHyphenVisible);
2565 							GlyphLayout hyphen;
2566 							hyphen.glyph = font.hyphenGlyph(charStyle);
2567 							hyphen.xadvance = font.hyphenWidth(charStyle, charStyle.fontSize() / 10.0);
2568 							hyphWidth = hyphen.xadvance * scaleH;
2569 							current.glyphs[currentIndex].append(hyphen);
2570 						}
2571 						else
2572 						{
2573 							if (itemText.text(a) != '-')
2574 								current.hyphenCount = 0;
2575 							if (current.glyphs[currentIndex].hasFlag(ScLayout_SoftHyphenVisible))
2576 							{
2577 								current.glyphs[currentIndex].clearFlag(ScLayout_SoftHyphenVisible);
2578 								current.glyphs[currentIndex].glyphs().removeLast();
2579 							}
2580 						}
2581 
2582 						// Justification
2583 						if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2584 							current.lineData.width += current.opticalRightMargin(itemText);
2585 						// #12565: Right alignment of hyphens
2586 						// The additional character width has already been taken into account
2587 						// above via the line break position, so it's not necessary to increase
2588 						// the line natural width again:
2589 						// line 2604: breakPos = current.xPos;
2590 						// line 2605: if (currentCh != '-')
2591 						// line 2606: {
2592 						// line 2607: 	breakPos += hyphWidth;
2593 						// line 2608: }
2594 						/*else
2595 							current.line.naturalWidth += hyphWidth;*/
2596 
2597 						OFs = 0;
2598 						if (style.alignment() == ParagraphStyle::RightAligned)
2599 							OFs = current.lineData.width - current.lineData.naturalWidth;
2600 						if (style.alignment() == ParagraphStyle::Centered)
2601 							OFs = (current.lineData.width - current.lineData.naturalWidth) / 2;
2602 						if (style.alignment() == ParagraphStyle::Justified && style.direction() == ParagraphStyle::RTL)
2603 							OFs = current.lineData.width - current.lineData.naturalWidth;
2604 
2605 						if ((style.alignment() == ParagraphStyle::Justified) || (style.alignment() == ParagraphStyle::Extended))
2606 							current.justifyLine(style);
2607 						else
2608 						{
2609 							if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2610 								current.lineData.naturalWidth += current.opticalRightMargin(itemText);
2611 							current.indentLine(style, OFs);
2612 						}
2613 						current.xPos = current.lineData.x + current.lineData.width;
2614 					}
2615 				}
2616 				if (inOverflow)
2617 					current.xPos = realEnd;
2618 
2619 				// line break and drop caps
2620 				// #11250: in case of a forced line break, we must not stop
2621 				// the drop cap layout process. This break case such as
2622 				// layout of poetry.
2623 				if (itemText.isBlockStart(a + 1) && current.hasDropCap)
2624 				{
2625 					current.hasDropCap = false;
2626 					if (current.yPos < maxDY)
2627 						current.yPos = maxDY;
2628 					maxDX = 0;
2629 				}
2630 				// end of column
2631 				if (current.isEndOfCol(desc))
2632 				{
2633 					//check if really line extends bottom margin
2634 					current.updateHeightMetrics();
2635 					if (current.isEndOfCol(current.lineData.descent))
2636 					{
2637 						if (current.isEmpty || current.column + 1 == m_columns)
2638 						{
2639 							goNoRoom = true;
2640 							m_maxChars = a + 1;
2641 							break;
2642 						}
2643 						goNextColumn = true;
2644 					}
2645 					else
2646 						setMaxY(maxYDesc);
2647 				}
2648 				else
2649 					setMaxY(maxYDesc);
2650 				if (current.lineData.firstCluster <= current.lineData.lastCluster && !current.isEmpty)
2651 				{
2652 					if (current.addLine && current.breakIndex >= 0)
2653 					{
2654 						if (current.glyphs[0].hasFlag(ScLayout_DropCap))
2655 						{
2656 							// put line back to top
2657 							current.lineData.y -= DropCapDrop;
2658 							current.glyphs[0].yoffset += DropCapDrop;
2659 						}
2660 						current.fillInTabLeaders();
2661 						//if right margin is set we temporally save line, not append it
2662 						textLayout.appendLine(current.createLineBox());
2663 						setMaxY(maxYDesc);
2664 						current.restartIndex = current.lineData.lastCluster + 1;
2665 						i = current.lineData.lastCluster;
2666 						currentIndex = i - current.lineData.firstCluster;
2667 						a = current.glyphs[currentIndex].firstChar();
2668 						current.rowDesc = qMax(current.rowDesc,current.yPos + current.lineData.descent);
2669 					//	if (glyphClusters[current.line.firstCluster].hasFlag(ScLayout_DropCap))
2670 					//		current.rowDesc = qMax(current.rowDesc,current.yPos + current.line.descent);
2671 					//	else
2672 							current.rowDesc = qMax(current.rowDesc,current.yPos - current.lineData.descent);
2673 						if (!current.lastInRowLine)
2674 						{
2675 							current.restartX = current.xPos;
2676 							if (current.addLeftIndent)
2677 							{
2678 								current.addLeftIndent = false;
2679 								current.wasFirstInRow = true;
2680 							}
2681 						}
2682 					}
2683 					else if (!inOverflow && !current.afterOverflow && current.lastInRowLine)
2684 					{
2685 						current.yPos++;
2686 						i = current.restartLine(true, true);
2687 						inOverflow = false;
2688 						outs = false;
2689 						current.mustLineEnd = current.colRight;
2690 						continue;
2691 					}
2692 				}
2693 				if (current.addLine && current.lastInRowLine)
2694 				{
2695 					current.recalculateY = true;
2696 					current.wasFirstInRow = false;
2697 					current.addLeftIndent = true;
2698 					inOverflow = false;
2699 					outs = false;
2700 					current.startOfCol = false;
2701 					current.restartX = current.xPos = qMax(maxDX, current.colLeft);
2702 					lastLineY = current.rowDesc;
2703 					if (current.hasDropCap)
2704 					{
2705 						++DropLinesCount;
2706 						if (DropLinesCount >= DropLines)
2707 						{
2708 							current.hasDropCap = false;
2709 							current.restartX = current.xPos = current.colLeft;
2710 							maxDX = 0;
2711 						}
2712 					}
2713 					lastLineY = current.rowDesc;
2714 					current.mustLineEnd = current.colRight;
2715 					current.restartRowIndex = current.restartIndex;
2716 				}
2717 				if ( SpecialChars::isBreak(itemText.text(a)) )
2718 				{
2719 					if (itemText.isBlockStart(a + 1))
2720 						current.yPos += style.gapAfter();
2721 					current.hyphenCount = 0;
2722 				}
2723 				if (inOverflow)
2724 				{
2725 					current.afterOverflow = true;
2726 					inOverflow = false;
2727 					current.addLeftIndent = false;
2728 					current.recalculateY = false;
2729 				}
2730 				else
2731 				{
2732 					current.afterOverflow = false;
2733 				}
2734 				outs = false;
2735 				current.addLine = false;
2736 				current.lastInRowLine = false;
2737 				// WTF does i + 1 mean here, what if i is the last run we have!
2738 				current.startLine(i + 1);
2739 				if (goNoRoom)
2740 				{
2741 					goNoRoom = false;
2742 					m_maxChars = a + 1;
2743 					goto NoRoom;
2744 				}
2745 				if (goNextColumn)
2746 				{
2747 					goNextColumn = false;
2748 					current.column++;
2749 					if (current.column < m_columns)
2750 					{
2751 						if (firstLineOffset() == FLOPRealGlyphHeight)
2752 							asce = 0;
2753 						current.nextColumn(textLayout);
2754 						current.mustLineEnd = current.colRight;
2755 						current.addLeftIndent = true;
2756 						lastLineY = m_textDistanceMargins.top();
2757 						current.rowDesc = 0;
2758 						current.recalculateY = true;
2759 					}
2760 					else
2761 					{
2762 						m_maxChars = a; // Always a + 1???
2763 						if (itemText.text(a) == SpecialChars::COLBREAK)
2764 							++m_maxChars;
2765 						goto NoRoom;
2766 					}
2767 				}
2768 			}
2769 			if (!shapedText.haveMoreText(i + 1, glyphClusters))
2770 			{
2771 				if (!current.afterOverflow || current.addLine)
2772 				{
2773 					current.addLine = true;
2774 					current.lastInRowLine = true;
2775 //					qDebug() << "breakline A no more text @" << i;
2776 					current.breakLine(i);
2777 				}
2778 				if (current.afterOverflow && !current.addLine)
2779 				{
2780 					if (current.restartIndex < current.lineData.firstCluster)
2781 					{
2782 						i = current.restartLine(false,true);
2783 						continue;
2784 					}
2785 //					qDebug() << "breakline B no more text @" << i;
2786 					current.breakLine(i);
2787 				}
2788 			}
2789 		}
2790 		if (goNoRoom)
2791 		{
2792 			goNoRoom = false;
2793 			goto NoRoom;
2794 		}
2795 // end of itemText
2796 // now place the last line
2797 		if (!current.isEmpty)
2798 		{
2799 			int a = itemText.length() - 1;
2800 			int i = glyphClusters.length() - 1;
2801 //			qDebug() << "breakline end of text @" << i;
2802 			current.breakLine(i);
2803 
2804 			if (current.startOfCol)
2805 			{
2806 				double addAsce;
2807 				if (DropCmode)
2808 					addAsce = qMax(realAsce, asce + offset);
2809 				else
2810 					addAsce = asce + offset;
2811 				if (style.lineSpacingMode() != ParagraphStyle::BaselineGridLineSpacing)
2812 				{
2813 					if (firstLineOffset() == FLOPRealGlyphHeight)
2814 						addAsce = realAsce;
2815 					else if (firstLineOffset() == FLOPLineSpacing)
2816 					{
2817 						if (DropCmode)
2818 							addAsce = DropCapDrop + style.lineSpacing();
2819 						else
2820 							addAsce = style.lineSpacing();
2821 					}
2822 				}
2823 				maxYAsc = current.yPos - addAsce;
2824 			}
2825 			else
2826 				maxYAsc = current.yPos - realAsce;
2827 			//fix for glyphs with negative realAsce value
2828 			maxYAsc = qMax(maxYAsc, 0.0);
2829 			maxYDesc = current.yPos + realDesc;
2830 
2831 			if (style.lineSpacingMode() != ParagraphStyle::AutomaticLineSpacing)
2832 			{
2833 				// #11727, #11628, etc.
2834 				maxYAsc = qMax(0.0, qMin(maxYAsc, current.yPos - asce));
2835 				maxYDesc = current.yPos + desc;
2836 			}
2837 
2838 			regionMinY = static_cast<int>(floor(maxYAsc));
2839 			regionMaxY = static_cast<int>(floor(maxYDesc));
2840 
2841 			EndX = current.endOfLine(m_availableRegion, style.rightMargin(), regionMinY, regionMaxY);
2842 			current.finishLine(EndX - current.rightIndent);
2843 
2844 			if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2845 				current.lineData.width += current.opticalRightMargin(itemText);
2846 
2847 			OFs = 0;
2848 			if (style.alignment() == ParagraphStyle::RightAligned)
2849 				OFs = current.lineData.width - current.lineData.naturalWidth;
2850 			if (style.alignment() == ParagraphStyle::Centered)
2851 				OFs = (current.lineData.width - current.lineData.naturalWidth) / 2;
2852 			if (style.alignment() == ParagraphStyle::Justified ||
2853 				style.alignment() == ParagraphStyle::Extended)
2854 			{
2855 				if (style.direction() == ParagraphStyle::RTL)
2856 					OFs = current.lineData.width - current.lineData.naturalWidth;
2857 				else
2858 					OFs = 0;
2859 			}
2860 
2861 			if (style.alignment() == ParagraphStyle::Extended
2862 				|| (style.alignment() == ParagraphStyle::Justified
2863 					&&  (itemText.text(a) == SpecialChars::LINEBREAK ||
2864 						 itemText.text(a) == SpecialChars::FRAMEBREAK ||
2865 						 itemText.text(a) == SpecialChars::COLBREAK)
2866 					&&  !itemText.text(a).isSpace()))
2867 			{
2868 				current.justifyLine(style);
2869 			}
2870 			else
2871 			{
2872 				if (opticalMargins & ParagraphStyle::OM_RightHangingPunct)
2873 					current.lineData.naturalWidth += current.opticalRightMargin(itemText);
2874 				current.indentLine(style, OFs);
2875 			}
2876 			if (current.glyphs[0].hasFlag(ScLayout_DropCap))
2877 			{
2878 				// put line back to top
2879 				current.lineData.y -= DropCapDrop;
2880 				current.glyphs[0].yoffset += DropCapDrop;
2881 			}
2882 			current.fillInTabLeaders();
2883 			current.startOfCol = false;
2884 			goNextColumn = false;
2885 
2886 			textLayout.appendLine(current.createLineBox());
2887 			setMaxY(maxYDesc);
2888 			current.startOfCol = false;
2889 
2890 			if (moveLinesFromPreviousFrame ()) {
2891 				layout ();  // line moving ensures that this won't be an endless loop
2892 				itemText.blockSignals(false);
2893 				return;
2894 			}
2895 		}
2896 	}
2897 	m_maxChars = itemText.length();
2898 	if (verticalAlign > 0)
2899 	{
2900 		double hAdjust = height() - textLayout.box()->naturalHeight() - m_textDistanceMargins.bottom();
2901 		if (hAdjust > 0)
2902 		{
2903 			if (verticalAlign == 1)
2904 				hAdjust /= 2;
2905 			if (FrameType == 0) // Rectangular frame
2906 				textLayout.box()->moveBy(0, hAdjust);
2907 			else
2908 			{
2909 				int vertAlign = verticalAlign;
2910 				double topDist = m_textDistanceMargins.top();
2911 				m_textDistanceMargins.setTop(topDist + hAdjust);
2912 				verticalAlign = 0;
2913 				layout();
2914 				verticalAlign = vertAlign;
2915 				m_textDistanceMargins.setTop(topDist);
2916 			}
2917 		}
2918 	}
2919 	invalid = false;
2920 	if (!isNoteFrame() && (!m_Doc->notesList().isEmpty() || m_Doc->notesChanged()))
2921 	{ //if notes are used
2922 		UndoManager::instance()->setUndoEnabled(false);
2923 		NotesInFrameMap notesMap = updateNotesFrames(noteMarksPosMap);
2924 		if (notesMap != m_notesFramesMap)
2925 		{
2926 			updateNotesMarks(notesMap);
2927 			notesFramesLayout();
2928 		}
2929 		UndoManager::instance()->setUndoEnabled(true);
2930 	}
2931 	if (m_nextBox != nullptr)
2932 	{
2933 		PageItem_TextFrame * nextFrame = dynamic_cast<PageItem_TextFrame*>(m_nextBox);
2934 		while (nextFrame)
2935 		{
2936 			nextFrame->invalid   = true;
2937 			nextFrame->firstChar = m_maxChars;
2938 			nextFrame = dynamic_cast<PageItem_TextFrame*>(nextFrame->m_nextBox);
2939 		}
2940 	}
2941 	itemText.blockSignals(false);
2942 //	qDebug("textframe: len=%d, done relayout", itemText.length());
2943 	return;
2944 
2945 NoRoom:
2946 	invalid = false;
2947 
2948 	adjustParagraphEndings ();
2949 
2950 	if (verticalAlign > 0)
2951 	{
2952 		double hAdjust = height() - textLayout.box()->naturalHeight() - m_textDistanceMargins.bottom();
2953 		if (hAdjust > 0)
2954 		{
2955 			if (verticalAlign == 1)
2956 				hAdjust /= 2;
2957 			if (FrameType == 0) // Rectangular frame
2958 				textLayout.box()->moveBy(0, hAdjust);
2959 			else
2960 			{
2961 				int vertAlign = verticalAlign;
2962 				double topDist = m_textDistanceMargins.top();
2963 				m_textDistanceMargins.setTop(topDist + hAdjust);
2964 				verticalAlign = 0;
2965 				layout();
2966 				verticalAlign = vertAlign;
2967 				m_textDistanceMargins.setTop(topDist);
2968 			}
2969 		}
2970 	}
2971 
2972 	if (!isNoteFrame() && (!m_Doc->notesList().isEmpty() || m_Doc->notesChanged()))
2973 	{
2974 		UndoManager::instance()->setUndoEnabled(false);
2975 		NotesInFrameMap notesMap = updateNotesFrames(noteMarksPosMap);
2976 		if (notesMap != m_notesFramesMap)
2977 		{
2978 			updateNotesMarks(notesMap);
2979 			notesFramesLayout();
2980 		}
2981 		UndoManager::instance()->setUndoEnabled(true);
2982 	}
2983 
2984 	PageItem_TextFrame * next = dynamic_cast<PageItem_TextFrame*>(m_nextBox);
2985 	if (next != nullptr)
2986 	{
2987 		if (itemText.cursorPosition() > signed(m_maxChars))
2988 		{
2989 			int nCP = itemText.cursorPosition();
2990 			if (m_Doc->appMode == modeEdit)
2991 				next->itemText.setCursorPosition( qMax(nCP, signed(m_maxChars)) );
2992 		}
2993 		while (next)
2994 		{
2995 			next->invalid   = true;
2996 			next->firstChar = m_maxChars;
2997 			next = dynamic_cast<PageItem_TextFrame*>(next->m_nextBox);
2998 		}
2999 	}
3000 //	qDebug("textframe: len=%d, done relayout (no room %d)", itemText.length(), MaxChars);
3001 	itemText.blockSignals(false);
3002 }
3003 
invalidateLayout(bool wholeChain)3004 void PageItem_TextFrame::invalidateLayout(bool wholeChain)
3005 {
3006 	//const bool wholeChain = true;
3007 	invalid = true;
3008 	if (wholeChain)
3009 	{
3010 		PageItem *prevFrame = this->prevInChain();
3011 		while (prevFrame != nullptr)
3012 		{
3013 			prevFrame->invalid = true;
3014 			prevFrame = prevFrame->prevInChain();
3015 		}
3016 		PageItem *nextFrame = this->nextInChain();
3017 		while (nextFrame != nullptr)
3018 		{
3019 			nextFrame->invalid = true;
3020 			nextFrame = nextFrame->nextInChain();
3021 		}
3022 	}
3023 }
3024 
invalidateLayout(int firstChar)3025 void PageItem_TextFrame::invalidateLayout(int firstChar)
3026 {
3027 	int storyLen = itemText.length();
3028 	slotInvalidateLayout(firstChar, storyLen);
3029 }
3030 
slotInvalidateLayout(int firstItem,int)3031 void PageItem_TextFrame::slotInvalidateLayout(int firstItem, int /*endItem*/)
3032 {
3033 	PageItem* firstFrame = firstInChain();
3034 	firstItem = itemText.prevParagraph(firstItem);
3035 
3036 	PageItem_TextFrame* firstInvalid = dynamic_cast<PageItem_TextFrame*>(firstFrame);
3037 	while (firstInvalid)
3038 	{
3039 		if (firstInvalid->invalid)
3040 			break;
3041 		if (firstInvalid->firstChar <= firstItem && firstItem <= firstInvalid->m_maxChars)
3042 			break;
3043 		firstInvalid = dynamic_cast<PageItem_TextFrame*>(firstInvalid->m_nextBox);
3044 	}
3045 
3046 	PageItem_TextFrame* invalidFrame = firstInvalid;
3047 	while (invalidFrame)
3048 	{
3049 		invalidFrame->invalid = true;
3050 		invalidFrame = dynamic_cast<PageItem_TextFrame*>(invalidFrame->m_nextBox);
3051 	}
3052 }
3053 
isValidChainFromBegin()3054 bool PageItem_TextFrame::isValidChainFromBegin()
3055 {
3056 	if (invalid)
3057 		return false;
3058 	if (m_backBox == nullptr)
3059 		return !invalid;
3060 
3061 	PageItem* prev = prevInChain();
3062 	while (prev != nullptr)
3063 	{
3064 		if (prev->invalid)
3065 			return false;
3066 		prev = prev->prevInChain();
3067 	}
3068 	return true;
3069 }
3070 
DrawObj_Item(ScPainter * p,QRectF cullingArea)3071 void PageItem_TextFrame::DrawObj_Item(ScPainter *p, QRectF cullingArea)
3072 {
3073 	if (invalid)
3074 	{
3075 		if (isNoteFrame() && asNoteFrame()->deleteIt)
3076 		//do not layout notes frames which should be deleted
3077 			return;
3078 		layout();
3079 	}
3080 	if (invalid)
3081 		return;
3082 	if (m_Doc->RePos)
3083 		return;
3084 	QTransform pf2;
3085 	//	tTabValues.clear();
3086 	p->save(); //SA1
3087 
3088 	if (!isEmbedded)
3089 		pf2.translate(m_xPos, m_yPos);
3090 	pf2.rotate(m_rotation);
3091 
3092 	if (!m_Doc->layerOutline(m_layerID))
3093 	{
3094 		if ((fillColor() != CommonStrings::None) || (GrType != 0))
3095 		{
3096 			if (isAnnotation() && !((m_Doc->appMode == modeEdit) && (m_Doc->m_Selection->findItem(this) != -1)) && ((annotation().Type() == 2) || (annotation().Type() == 5) || (annotation().Type() == 6)))
3097 			{
3098 				if ((annotation().Feed() == 1) && annotation().IsOn())
3099 					p->setBrush(QColor(255 - m_fillQColor.red(), 255 - m_fillQColor.green(), 255 - m_fillQColor.blue(), m_fillQColor.alpha()));
3100 			}
3101 			p->setupPolygon(&PoLine);
3102 			p->fillPath();
3103 		}
3104 	}
3105 	MarginStruct savedTextDistanceMargins(m_textDistanceMargins);
3106 	if (isAnnotation() && !((m_Doc->appMode == modeEdit) && (m_Doc->m_Selection->findItem(this) != -1)) && (((annotation().Type() > 1) && (annotation().Type() < 11)) || (annotation().Type() > 12)))
3107 	{
3108 
3109 		if ((m_Doc->drawAsPreview && !m_Doc->editOnPreview) && ((annotation().Vis() == 1) || (annotation().Vis() == 3)))
3110 		{
3111 			p->restore();
3112 			return;
3113 		}
3114 		QColor fontColor;
3115 		SetQColor(&fontColor, itemText.defaultStyle().charStyle().fillColor(), itemText.defaultStyle().charStyle().fillShade());
3116 		double fontSize = itemText.defaultStyle().charStyle().fontSize() / 10.0;
3117 		ScFace font = itemText.defaultStyle().charStyle().font();
3118 		QString bmUtf16("");
3119 		QString cc;
3120 		if (!((itemText.length() == 1) && (itemText.text(0, 1) == QChar(13))))
3121 		{
3122 			for (int d = 0; d < itemText.length(); ++d)
3123 			{
3124 				if (d == 0)
3125 				{
3126 					const CharStyle& charStyle(itemText.charStyle(d));
3127 					SetQColor(&fontColor, charStyle.fillColor(), charStyle.fillShade());
3128 					fontSize = charStyle.fontSize() / 10.0;
3129 					font = charStyle.font();
3130 				}
3131 				cc = itemText.text(d, 1);
3132 				bmUtf16 += (cc == QChar(13) ? QChar(10) : cc);
3133 			}
3134 		}
3135 		p->save();
3136 		if ((annotation().borderWidth() > 0) && (annotation().borderColor() != CommonStrings::None) && (annotation().Type() != Annotation::Text))
3137 		{
3138 			QColor tmp;
3139 			SetQColor(&tmp, annotation().borderColor(), 100);
3140 			int borderStyle = annotation().borderStyle();
3141 			if (annotation().IsOn())
3142 			{
3143 				if (annotation().Feed() == 2)
3144 					tmp = QColor(255 - tmp.red(), 255 - tmp.green(), 255 - tmp.blue(), tmp.alpha());
3145 				if (annotation().Feed() == 3)
3146 					borderStyle = 4;
3147 			}
3148 			if (annotation().Type() == Annotation::RadioButton)
3149 			{
3150 				double bwh = annotation().borderWidth() / 2.0;
3151 				if (annotation().IsOn())
3152 				{
3153 					if (borderStyle == 4)
3154 						borderStyle = 3;
3155 					else
3156 						borderStyle = 4;
3157 				}
3158 				if ((borderStyle == 0) || (borderStyle == 1))
3159 				{
3160 					QPainterPath clp;
3161 					clp.addEllipse(QRectF(bwh, bwh, m_width - annotation().borderWidth(), m_height - annotation().borderWidth()));
3162 					FPointArray clpArr;
3163 					clpArr.fromQPainterPath(clp);
3164 					p->setupPolygon(&clpArr);
3165 					p->setPen(tmp, annotation().borderWidth(), borderStyle == 0 ? Qt::SolidLine : Qt::DashLine, Qt::FlatCap, Qt::MiterJoin);
3166 					p->setFillMode(ScPainter::None);
3167 					p->setStrokeMode(ScPainter::Solid);
3168 					p->strokePath();
3169 				}
3170 				else if (borderStyle == 3)
3171 					p->drawShadeCircle(QRectF(0, 0, m_width, m_height), tmp, false, annotation().borderWidth());
3172 				else if (borderStyle == 4)
3173 					p->drawShadeCircle(QRectF(0, 0, m_width, m_height), tmp, true, annotation().borderWidth());
3174 			}
3175 			else
3176 			{
3177 				if ((annotation().Type() == Annotation::Checkbox) && annotation().IsOn())
3178 				{
3179 					if (borderStyle == 4)
3180 						borderStyle = 3;
3181 					else
3182 						borderStyle = 4;
3183 				}
3184 				if (borderStyle == 2)
3185 					p->drawUnderlinedRect(QRectF(0, 0, m_width, m_height), tmp, annotation().borderWidth());
3186 				else if (borderStyle == 3)
3187 					p->drawShadePanel(QRectF(0, 0, m_width, m_height), tmp, false, annotation().borderWidth());
3188 				else if (borderStyle == 4)
3189 					p->drawShadePanel(QRectF(0, 0, m_width, m_height), tmp, true, annotation().borderWidth());
3190 				else
3191 				{
3192 					p->setPen(tmp, annotation().borderWidth(), borderStyle == 0 ? Qt::SolidLine : Qt::DashLine, Qt::FlatCap, Qt::MiterJoin);
3193 					p->setStrokeMode(ScPainter::Solid);
3194 					p->drawRect(0, 0, m_width, m_height);
3195 				}
3196 			}
3197 		}
3198 		if (annotation().Type() == Annotation::Button)
3199 		{
3200 			int wdt = annotation().borderWidth();
3201 			QPainterPath clp;
3202 			clp.addRect(QRectF(wdt, wdt, m_width - (2 * wdt), m_height - (2 * wdt)));
3203 			FPointArray clpArr;
3204 			clpArr.fromQPainterPath(clp);
3205 			p->setupPolygon(&clpArr);
3206 			p->setClipPath();
3207 			if (!bmUtf16.isEmpty())
3208 			{
3209 				if ((annotation().Feed() == 1) && annotation().IsOn())
3210 					p->setPen(QColor(255 - fontColor.red(), 255 - fontColor.green(), 255 - fontColor.blue(), fontColor.alpha()));
3211 				else
3212 					p->setPen(fontColor);
3213 				p->setFont(font, fontSize);
3214 				p->drawText(QRectF(wdt, wdt, m_width - (2 * wdt), m_height - (2 * wdt)), bmUtf16, false);
3215 			}
3216 			if ((!Pfile.isEmpty()) && (imageIsAvailable) && (m_imageVisible) && (annotation().UseIcons()))
3217 			{
3218 				p->save();//SA2
3219 				p->setupPolygon(&PoLine);
3220 				p->setClipPath();
3221 				p->scale(m_imageXScale, m_imageYScale);
3222 				p->translate(m_imageXOffset*m_imageXScale, m_imageYOffset*m_imageYScale);
3223 				p->rotate(m_imageRotation);
3224 				if (pixm.width() > 0 && pixm.height() > 0)
3225 					p->drawImage(pixm.qImagePtr());
3226 				p->restore();//RE2
3227 			}
3228 			p->restore();
3229 			p->restore();
3230 			return;
3231 		}
3232 		if (annotation().Type() == Annotation::Textfield)
3233 		{
3234 			int wdt = annotation().borderWidth();
3235 			m_textDistanceMargins.set(wdt, wdt, wdt, wdt);
3236 			invalid = true;
3237 			layout();
3238 		}
3239 		else if (annotation().Type() == Annotation::RadioButton)
3240 		{
3241 			if (annotation().IsChecked())
3242 			{
3243 				QPainterPath clp2;
3244 				double siz = qMin(width(), height()) * 0.4;
3245 				QRectF sizR(0, 0, siz, siz);
3246 				sizR.moveCenter(QPointF(width() / 2.0, height() / 2.0));
3247 				clp2.addEllipse(sizR);
3248 				FPointArray clpArr2;
3249 				clpArr2.fromQPainterPath(clp2);
3250 				p->setBrush(fontColor);
3251 				p->setFillMode(ScPainter::Solid);
3252 				p->setupPolygon(&clpArr2);
3253 				p->fillPath();
3254 			}
3255 			p->restore();
3256 			p->restore();
3257 			return;
3258 		}
3259 		else if (annotation().Type() == Annotation::Checkbox)
3260 		{
3261 			if (annotation().IsChecked())
3262 			{
3263 				p->setBrush(fontColor);
3264 				p->setFillMode(ScPainter::Solid);
3265 				p->setStrokeMode(ScPainter::None);
3266 				QPainterPath chkPath;
3267 				if (annotation().ChkStil() == 0)
3268 				{
3269 					chkPath.moveTo(6.60156, 0);
3270 					chkPath.lineTo(6.7725, 0.244063);
3271 					chkPath.cubicTo(6.7725, 0.244063, 5.7275, 1.03031, 4.44813, 2.66609);
3272 					chkPath.cubicTo(4.44813, 2.66609, 3.16891, 4.30172, 2.49516, 5.72266);
3273 					chkPath.lineTo(2.13375, 5.96672);
3274 					chkPath.cubicTo(2.13375, 5.96672, 1.68453, 6.27922, 1.52344, 6.43062);
3275 					chkPath.cubicTo(1.52344, 6.43062, 1.46, 6.20109, 1.24516, 5.67875);
3276 					chkPath.lineTo(1.10844, 5.36125);
3277 					chkPath.cubicTo(1.10844, 5.36125, 0.815469, 4.67766, 0.563906, 4.35062);
3278 					chkPath.cubicTo(0.563906, 4.35062, 0.3125, 4.02344, 0, 3.91594);
3279 					chkPath.cubicTo(0, 3.91594, 0.527344, 3.35938, 0.966875, 3.35938);
3280 					chkPath.cubicTo(0.966875, 3.35938, 1.34281, 3.35938, 1.80172, 4.37984);
3281 					chkPath.lineTo(1.95312, 4.72172);
3282 					chkPath.cubicTo(1.95312, 4.72172, 2.77828, 3.33, 4.07219, 2.01656);
3283 					chkPath.cubicTo(4.07219, 2.01656, 5.36625, 0.703125, 6.60156, 0);
3284 					chkPath.closeSubpath();
3285 				}
3286 				else if (annotation().ChkStil() == 1)
3287 				{
3288 					chkPath.moveTo(2.42672, 3.30078);
3289 					chkPath.lineTo(2.35359, 3.40328);
3290 					chkPath.cubicTo(2.35359, 3.40328, 1.59187, 4.55563, 1.10359, 4.55563);
3291 					chkPath.cubicTo(1.10359, 4.55563, 0.542031, 4.55563, 0, 3.64266);
3292 					chkPath.cubicTo(0, 3.64266, 0.0732812, 3.6475, 0.1075, 3.6475);
3293 					chkPath.cubicTo(0.1075, 3.6475, 0.747031, 3.6475, 1.53328, 2.67094);
3294 					chkPath.lineTo(1.665, 2.50484);
3295 					chkPath.lineTo(1.49422, 2.31937);
3296 					chkPath.cubicTo(1.49422, 2.31937, 0.776406, 1.55766, 0.776406, 1.02547);
3297 					chkPath.cubicTo(0.776406, 1.02547, 0.776406, 0.590781, 1.5625, 0);
3298 					chkPath.cubicTo(1.5625, 0, 1.67484, 0.756875, 2.23141, 1.47469);
3299 					chkPath.lineTo(2.37797, 1.66016);
3300 					chkPath.lineTo(2.49516, 1.50875);
3301 					chkPath.cubicTo(2.49516, 1.50875, 3.31062, 0.46875, 3.97469, 0.46875);
3302 					chkPath.cubicTo(3.97469, 0.46875, 4.49219, 0.46875, 4.76078, 1.25484);
3303 					chkPath.cubicTo(4.76078, 1.25484, 4.68266, 1.24516, 4.64844, 1.24516);
3304 					chkPath.cubicTo(4.64844, 1.24516, 4.41406, 1.24516, 3.98922, 1.56016);
3305 					chkPath.cubicTo(3.98922, 1.56016, 3.56453, 1.875, 3.27156, 2.27047);
3306 					chkPath.lineTo(3.125, 2.47078);
3307 					chkPath.lineTo(3.26656, 2.6075);
3308 					chkPath.cubicTo(3.26656, 2.6075, 4.05281, 3.36922, 4.90719, 3.36922);
3309 					chkPath.cubicTo(4.90719, 3.36922, 4.44828, 4.29687, 3.95016, 4.29687);
3310 					chkPath.cubicTo(3.95016, 4.29687, 3.50094, 4.29687, 2.65141, 3.50594);
3311 					chkPath.lineTo(2.42672, 3.30078);
3312 					chkPath.closeSubpath();
3313 				}
3314 				else if (annotation().ChkStil() == 2)
3315 				{
3316 					chkPath.moveTo(0, 4.09187);
3317 					chkPath.lineTo(2.89062, 0);
3318 					chkPath.lineTo(5.78125, 4.09187);
3319 					chkPath.lineTo(2.89062, 8.17875);
3320 					chkPath.closeSubpath();
3321 				}
3322 				else if (annotation().ChkStil() == 3)
3323 				{
3324 					chkPath.moveTo(0, 2.89062);
3325 					chkPath.cubicTo(0, 2.89062, 0, 1.69437, 0.847187, 0.847187);
3326 					chkPath.cubicTo(0.847187, 0.847187, 1.69437, 0, 2.89062, 0);
3327 					chkPath.cubicTo(2.89062, 0, 4.08703, 0, 4.93656, 0.847187);
3328 					chkPath.cubicTo(4.93656, 0.847187, 5.78625, 1.69437, 5.78625, 2.89062);
3329 					chkPath.cubicTo(5.78625, 2.89062, 5.78625, 4.08688, 4.93656, 4.93406);
3330 					chkPath.cubicTo(4.93656, 4.93406, 4.08703, 5.78125, 2.89062, 5.78125);
3331 					chkPath.cubicTo(2.89062, 5.78125, 1.69437, 5.78125, 0.847187, 4.93406);
3332 					chkPath.cubicTo(0.847187, 4.93406, 0, 4.08688, 0, 2.89062);
3333 					chkPath.closeSubpath();
3334 				}
3335 				else if (annotation().ChkStil() == 4)
3336 				{
3337 					chkPath.moveTo(0, 2.49516);
3338 					chkPath.lineTo(2.62219, 2.49516);
3339 					chkPath.lineTo(3.43266, 0);
3340 					chkPath.lineTo(4.24328, 2.49516);
3341 					chkPath.lineTo(6.86531, 2.49516);
3342 					chkPath.lineTo(4.74609, 4.03812);
3343 					chkPath.lineTo(5.55672, 6.53313);
3344 					chkPath.lineTo(3.43266, 4.99016);
3345 					chkPath.lineTo(1.30859, 6.53313);
3346 					chkPath.lineTo(2.11922, 4.03812);
3347 					chkPath.closeSubpath();
3348 				}
3349 				else if (annotation().ChkStil() == 5)
3350 				{
3351 					chkPath.moveTo(0, 5.78125);
3352 					chkPath.lineTo(0, 0);
3353 					chkPath.lineTo(5.78125, 0);
3354 					chkPath.lineTo(5.78125, 5.78125);
3355 					chkPath.closeSubpath();
3356 				}
3357 				if (!chkPath.isEmpty())
3358 				{
3359 					QTransform mm;
3360 					mm.scale(fontSize / 10.0, fontSize / 10.0);
3361 					chkPath = mm.map(chkPath);
3362 					QRectF bb = chkPath.boundingRect();
3363 					QRectF bi = QRectF(0.0, 0.0, m_width, m_height);
3364 					double dx = bi.center().x() - (bb.width() / 2.0);
3365 					double dy = bi.center().y() - (bb.height() / 2.0);
3366 					p->translate(dx, dy);
3367 					FPointArray chArr;
3368 					chArr.fromQPainterPath(chkPath);
3369 					p->setupPolygon(&chArr);
3370 					p->fillPath();
3371 				}
3372 				p->restore();
3373 				p->restore();
3374 				return;
3375 			}
3376 		}
3377 		else if (annotation().Type() == Annotation::Combobox)
3378 		{
3379 			int wdt = annotation().borderWidth();
3380 			if (m_width > 2 * wdt + 15)
3381 			{
3382 				if (!bmUtf16.isEmpty())
3383 				{
3384 					p->save();
3385 					QPainterPath clp;
3386 					clp.addRect(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2));
3387 					FPointArray clpArr;
3388 					clpArr.fromQPainterPath(clp);
3389 					p->setupPolygon(&clpArr);
3390 					p->setClipPath();
3391 					p->setPen(fontColor);
3392 					p->setFont(font, fontSize);
3393 					QStringList textList = bmUtf16.split("\n");
3394 					p->drawText(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2), textList[0], false, 1);
3395 					p->restore();
3396 				}
3397 				p->setFillMode(ScPainter::Solid);
3398 				p->setStrokeMode(ScPainter::None);
3399 				p->setBrush(QColor(200, 200, 200));
3400 				QRectF bi;
3401 				if ((annotation().borderStyle() == 3) || (annotation().borderStyle() == 4))
3402 					bi = QRectF(m_width - wdt - 15, wdt, 15, m_height - (2 * wdt));
3403 				else
3404 					bi = QRectF(m_width - (wdt / 2.0) - 15, wdt / 2.0, 15, m_height - wdt);
3405 				p->drawRect(bi.x(), bi.y(), bi.width(), bi.height());
3406 				QPainterPath chkPath;
3407 				chkPath.moveTo(bi.center().x() - 3, bi.center().y());
3408 				chkPath.lineTo(bi.center().x() + 3, bi.center().y());
3409 				chkPath.lineTo(bi.center().x(), bi.center().y() + 4);
3410 				chkPath.closeSubpath();
3411 				FPointArray chArr;
3412 				chArr.fromQPainterPath(chkPath);
3413 				p->setupPolygon(&chArr);
3414 				p->setBrush(QColor(0, 0, 0));
3415 				p->fillPath();
3416 				p->restore();
3417 				p->restore();
3418 				return;
3419 			}
3420 		}
3421 		else if (annotation().Type() == Annotation::Listbox)
3422 		{
3423 			int wdt = annotation().borderWidth();
3424 			if (m_width > 2 * wdt + 15)
3425 			{
3426 				if (!bmUtf16.isEmpty())
3427 				{
3428 					p->save();
3429 					QPainterPath clp;
3430 					clp.addRect(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2));
3431 					FPointArray clpArr;
3432 					clpArr.fromQPainterPath(clp);
3433 					p->setupPolygon(&clpArr);
3434 					p->setClipPath();
3435 					p->setPen(fontColor);
3436 					p->setFont(font, fontSize);
3437 					p->drawText(QRectF(wdt + 1, wdt + 1, m_width - (2 * wdt) - 17, m_height - (2 * wdt) - 2), bmUtf16, false, 2);
3438 					p->restore();
3439 				}
3440 				p->restore();
3441 				p->restore();
3442 				return;
3443 			}
3444 		}
3445 		else if (annotation().Type() == Annotation::Text)
3446 		{
3447 			if (m_Doc->drawAsPreview && !m_Doc->editOnPreview)
3448 			{
3449 				if (annotation().IsOpen())
3450 				{
3451 					p->save();
3452 					p->translate(m_origAnnotPos.x() - xPos(), m_origAnnotPos.y() - yPos());
3453 					drawNoteIcon(p);
3454 					p->restore();
3455 					p->save();
3456 					double basX = 15;
3457 					double basY = 15;
3458 					QPainterPath clp;
3459 					clp.addRoundedRect(basX, basY, 250, 250, 5, 5);
3460 					p->setBrush(QColor(240, 240, 0));
3461 					p->setFillMode(ScPainter::Solid);
3462 					p->setStrokeMode(ScPainter::None);
3463 					FPointArray clpArr;
3464 					clpArr.fromQPainterPath(clp);
3465 					p->setupPolygon(&clpArr);
3466 					p->fillPath();
3467 					p->setPen(QColor(0, 0, 0), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3468 					p->setBrush(QColor(255, 255, 255));
3469 					p->setStrokeMode(ScPainter::Solid);
3470 					p->drawRect(basX + 10, basY + 20, 230, 220);
3471 					p->setFillMode(ScPainter::None);
3472 					p->drawRect(basX + 230, basY + 5, 11, 11);
3473 					p->setPen(QColor(0, 0, 0), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3474 					p->drawLine(QPointF(basX + 232, basY + 13), QPointF(basX + 239, basY + 13));
3475 					p->save();
3476 					p->translate(basX + 10, basY + 5);
3477 					p->scale(0.5, 0.5);
3478 					drawNoteIcon(p);
3479 					p->restore();
3480 					clp = QPainterPath();
3481 					clp.addRect(basX + 10, basY + 20, 230, 220);
3482 					clpArr.fromQPainterPath(clp);
3483 					p->setupPolygon(&clpArr);
3484 					p->setClipPath();
3485 					p->setPen(fontColor);
3486 					p->setFont(font, fontSize);
3487 					p->drawText(QRectF(basX + 11, basY + 21, 228, 218), bmUtf16, false, 2);
3488 					p->restore();
3489 				}
3490 				else
3491 					drawNoteIcon(p);
3492 				p->restore();
3493 				p->restore();
3494 				return;
3495 			}
3496 		}
3497 		p->restore();
3498 	}
3499 	/* Experimental Addition to display an Image as Background */
3500 	/*
3501 	else if ((!Pfile.isEmpty()) && (PictureIsAvailable) && (PicArt))
3502 	{
3503 		p->save();
3504 		if (imageClip.size() != 0)
3505 		{
3506 			p->setupPolygon(&imageClip);
3507 			p->setClipPath();
3508 		}
3509 		p->setupPolygon(&PoLine);
3510 		p->setClipPath();
3511 		if (imageFlippedH())
3512 		{
3513 			p->translate(Width, 0);
3514 			p->scale(-1, 1);
3515 		}
3516 		if (imageFlippedV())
3517 		{
3518 			p->translate(0, Height);
3519 			p->scale(1, -1);
3520 		}
3521 		p->translate(LocalX*LocalScX, LocalY*LocalScY);
3522 		p->scale(LocalScX, LocalScY);
3523 		if (pixm.imgInfo.lowResType != 0)
3524 			p->scale(pixm.imgInfo.lowResScale, pixm.imgInfo.lowResScale);
3525 		p->drawImage(pixm.qImagePtr());
3526 		p->restore();
3527 	}
3528 */
3529 
3530 	if (itemText.length() != 0)
3531 	{
3532 		//		qDebug("drawing textframe: len=%d", itemText.length());
3533 		if (imageFlippedH())
3534 		{
3535 			p->translate(m_width, 0);
3536 			p->scale(-1, 1);
3537 			pf2.translate(m_width, 0);
3538 			pf2.scale(-1, 1);
3539 		}
3540 		if (imageFlippedV())
3541 		{
3542 			p->translate(0, m_height);
3543 			p->scale(1, -1);
3544 			pf2.translate(0, m_height);
3545 			pf2.scale(1, -1);
3546 		}
3547 		assert( firstInFrame() >= 0 );
3548 		assert( lastInFrame() < itemText.length() );
3549 
3550 		int fm = p->fillMode();
3551 		int sm = p->strokeMode();
3552 		p->setFillMode(ScPainter::Solid);
3553 		p->setStrokeMode(ScPainter::Solid);
3554 		ScreenPainter painter(p, this);
3555 		textLayout.renderBackground(&painter);
3556 		textLayout.render(&painter, this);
3557 		p->setFillMode(fm);
3558 		p->setStrokeMode(sm);
3559 	}
3560 	m_textDistanceMargins=savedTextDistanceMargins;
3561 	p->restore();//RE1
3562 }
3563 
DrawObj_Post(ScPainter * p)3564 void PageItem_TextFrame::DrawObj_Post(ScPainter *p)
3565 {
3566 	if (m_Doc->layerOutline(m_layerID))
3567 	{
3568 		p->setPen(m_Doc->layerMarker(m_layerID), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3569 		p->setFillMode(ScPainter::None);
3570 		p->setBrushOpacity(1.0);
3571 		p->setPenOpacity(1.0);
3572 		p->setupPolygon(&PoLine);
3573 		p->strokePath();
3574 	}
3575 	else
3576 	{
3577 		if (fillBlendmode() != 0)
3578 			p->setBlendModeFill(0);
3579 		p->setMaskMode(0);
3580 
3581 		if (!isAnnotation() || (isAnnotation() && ((annotation().Type() == 0) || (annotation().Type() > 6))))
3582 		{
3583 			p->setBlendModeStroke(lineBlendmode());
3584 			p->setPenOpacity(1.0 - lineTransparency());
3585 			p->setupPolygon(&PoLine);
3586 			if (NamedLStyle.isEmpty())
3587 			{
3588 				if ((lineColor() != CommonStrings::None) || (!patternStrokeVal.isEmpty()) || (GrTypeStroke > 0))
3589 				{
3590 					p->setPen(m_strokeQColor, m_lineWidth, PLineArt, PLineEnd, PLineJoin);
3591 					if (DashValues.count() != 0)
3592 						p->setDash(DashValues, DashOffset);
3593 				}
3594 				ScPattern *strokePattern = m_Doc->checkedPattern(patternStrokeVal);
3595 				if (strokePattern)
3596 				{
3597 					if (patternStrokePath)
3598 					{
3599 						QPainterPath guidePath = PoLine.toQPainterPath(false);
3600 						DrawStrokePattern(p, guidePath);
3601 					}
3602 					else
3603 					{
3604 						p->setPattern(&m_Doc->docPatterns[patternStrokeVal], patternStrokeScaleX, patternStrokeScaleY, patternStrokeOffsetX, patternStrokeOffsetY, patternStrokeRotation, patternStrokeSkewX, patternStrokeSkewY, patternStrokeMirrorX, patternStrokeMirrorY);
3605 						p->setStrokeMode(ScPainter::Pattern);
3606 						p->strokePath();
3607 					}
3608 				}
3609 				else if (GrTypeStroke > 0)
3610 				{
3611 					if ((!gradientStrokeVal.isEmpty()) && (!m_Doc->docGradients.contains(gradientStrokeVal)))
3612 						gradientStrokeVal.clear();
3613 					if (!(gradientStrokeVal.isEmpty()) && (m_Doc->docGradients.contains(gradientStrokeVal)))
3614 						stroke_gradient = m_Doc->docGradients[gradientStrokeVal];
3615 					if (stroke_gradient.stops() < 2) // fall back to solid stroking if there are not enough colorstops in the gradient.
3616 					{
3617 						if (lineColor() != CommonStrings::None)
3618 						{
3619 							p->setBrush(m_strokeQColor);
3620 							p->setStrokeMode(ScPainter::Solid);
3621 						}
3622 						else
3623 						{
3624 							no_stroke = true;
3625 							p->setStrokeMode(ScPainter::None);
3626 						}
3627 					}
3628 					else
3629 					{
3630 						p->setStrokeMode(ScPainter::Gradient);
3631 						p->stroke_gradient = stroke_gradient;
3632 						if (GrTypeStroke == Gradient_Linear)
3633 							p->setGradient(VGradient::linear, FPoint(GrStrokeStartX, GrStrokeStartY), FPoint(GrStrokeEndX, GrStrokeEndY), FPoint(GrStrokeStartX, GrStrokeStartY), GrStrokeScale, GrStrokeSkew);
3634 						else
3635 							p->setGradient(VGradient::radial, FPoint(GrStrokeStartX, GrStrokeStartY), FPoint(GrStrokeEndX, GrStrokeEndY), FPoint(GrStrokeFocalX, GrStrokeFocalY), GrStrokeScale, GrStrokeSkew);
3636 					}
3637 					p->strokePath();
3638 				}
3639 				else if (lineColor() != CommonStrings::None)
3640 				{
3641 					p->setStrokeMode(ScPainter::Solid);
3642 					p->setPen(m_strokeQColor, m_lineWidth, PLineArt, PLineEnd, PLineJoin);
3643 					if (DashValues.count() != 0)
3644 						p->setDash(DashValues, DashOffset);
3645 					p->strokePath();
3646 				}
3647 				else
3648 					no_stroke = true;
3649 			}
3650 			else
3651 			{
3652 				p->setStrokeMode(ScPainter::Solid);
3653 				multiLine ml = m_Doc->docLineStyles[NamedLStyle];
3654 				QColor tmp;
3655 				for (int it = ml.size() - 1; it > -1; it--)
3656 				{
3657 					SetQColor(&tmp, ml[it].Color, ml[it].Shade);
3658 					p->setPen(tmp, ml[it].Width,
3659 								static_cast<Qt::PenStyle>(ml[it].Dash),
3660 								static_cast<Qt::PenCapStyle>(ml[it].LineEnd),
3661 								static_cast<Qt::PenJoinStyle>(ml[it].LineJoin));
3662 					p->strokePath();
3663 				}
3664 			}
3665 			if (lineBlendmode() != 0)
3666 				p->setBlendModeStroke(0);
3667 		}
3668 		else if (isAnnotation())
3669 		{
3670 			if (annotation().borderColor() == CommonStrings::None)
3671 				no_stroke = true;
3672 		}
3673 	}
3674 	p->setFillMode(ScPainter::Solid);
3675 	p->setStrokeMode(ScPainter::Solid);
3676 	p->restore();
3677 }
3678 
DrawObj_Decoration(ScPainter * p)3679 void PageItem_TextFrame::DrawObj_Decoration(ScPainter *p)
3680 {
3681 	//#12405 if (isAnnotation() && ((annotation().Type() > 1) && (annotation().Type() < 7)) && (annotation().borderWidth() > 0))
3682 	//	return;
3683 	p->save();
3684 //	p->setAntialiasing(false);
3685 	if (!isEmbedded)
3686 		p->translate(m_xPos, m_yPos);
3687 	p->rotate(m_rotation);
3688 	if ((!isEmbedded) && (!m_Doc->RePos))
3689 	{
3690 		double scpInv = 0.0;
3691 		if ((drawFrame()) && (m_Doc->guidesPrefs().framesShown) && (no_stroke))
3692 		{
3693 			p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameNormColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3694 			if ((isBookmark) || (m_isAnnotation))
3695 				p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameAnnotationColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3696 			if ((m_backBox != nullptr) || (m_nextBox != nullptr))
3697 				p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameLinkColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3698 			if (m_Locked)
3699 				p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameLockColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3700 
3701 			p->setFillMode(ScPainter::None);
3702 			p->setupSharpPolygon(&PoLine);
3703 			p->strokePath();
3704 		}
3705 		if ((m_Doc->guidesPrefs().framesShown) && textFlowUsesContourLine() && (!ContourLine.empty()))
3706 		{
3707 			p->setPen(Qt::darkGray, 0, Qt::DotLine, Qt::FlatCap, Qt::MiterJoin);
3708 			p->setupSharpPolygon(&ContourLine);
3709 			p->strokePath();
3710 		}
3711 
3712 		//Draw the overflow icon
3713 		if (frameOverflows())
3714 		{
3715 			if ((!m_Doc->drawAsPreview) && (!isAnnotation()))
3716 				drawOverflowMarker(p);
3717 		}
3718 		if ((m_Doc->guidesPrefs().colBordersShown) && (!m_Doc->drawAsPreview))
3719 			drawColumnBorders(p);
3720 		if ((m_Doc->guidesPrefs().layerMarkersShown) &&
3721 			(m_Doc->layerCount() > 1) &&
3722 			(!m_Doc->layerOutline(m_layerID)) &&
3723 			(!m_Doc->drawAsPreview))
3724 		{
3725 			p->setPen(Qt::black, 0, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3726 			p->setPenOpacity(1.0);
3727 			p->setBrush(m_Doc->layerMarker(m_layerID));
3728 			p->setBrushOpacity(1.0);
3729 			p->setFillMode(ScPainter::Solid);
3730 			double ofwh = 10;
3731 			double ofx = m_width - ofwh/2;
3732 			double ofy = m_height - ofwh*3;
3733 			p->drawSharpRect(ofx, ofy, ofwh, ofwh);
3734 		}
3735 		if (no_fill && no_stroke && m_Doc->guidesPrefs().framesShown)
3736 		{
3737 			p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameNormColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3738 			if (m_Locked)
3739 				p->setPen(PrefsManager::instance().appPrefs.displayPrefs.frameLockColor, scpInv, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
3740 			p->setFillMode(ScPainter::None);
3741 			p->drawSharpRect(0, 0, m_width, m_height);
3742 			no_fill = false;
3743 			no_stroke = false;
3744 		}
3745 		//if (m_Doc->selection->findItem(this) != -1)
3746 		//	drawLockedMarker(p);
3747 	}
3748 	FrameOnly = false;
3749 //	p->setAntialiasing(true);
3750 	p->restore();
3751 }
3752 
3753 
clearContents()3754 void PageItem_TextFrame::clearContents()
3755 {
3756 	if (itemText.length() <= 0)
3757 		return;
3758 
3759 	PageItem *nextItem = this;
3760 	while (nextItem->prevInChain() != nullptr)
3761 		nextItem = nextItem->prevInChain();
3762 
3763 	ParagraphStyle defaultStyle = nextItem->itemText.defaultStyle();
3764 	nextItem->itemText.selectAll();
3765 	nextItem->asTextFrame()->deleteSelectedTextFromFrame();
3766 	if (!isNoteFrame())
3767 	{
3768 		if (UndoManager::undoEnabled() && undoManager->getLastUndo())
3769 			undoManager->getLastUndo()->setName(Um::ClearText);
3770 		nextItem->itemText.setDefaultStyle(defaultStyle);
3771 	}
3772 
3773 	while (nextItem != nullptr)
3774 	{
3775 		nextItem->invalid = true;
3776 		nextItem = nextItem->nextInChain();
3777 	}
3778 }
3779 
truncateContents()3780 void PageItem_TextFrame::truncateContents()
3781 {
3782 	if ((this->nextInChain() == nullptr) && frameOverflows())
3783 	{
3784 		ParagraphStyle defaultStyle = this->itemText.defaultStyle();
3785 		int pos = itemText.cursorPosition();
3786 		if (!itemText.hasSelection())
3787 		{
3788 			itemText.select(maxCharsInFrame(), itemText.length() - maxCharsInFrame(), true);
3789 			deleteSelectedTextFromFrame();
3790 		}
3791 		itemText.setCursorPosition(pos, true);
3792 
3793 		if (UndoManager::undoEnabled() && undoManager->getLastUndo())
3794 			undoManager->getLastUndo()->setName(Um::TruncateText);
3795 		this->itemText.setDefaultStyle(defaultStyle);
3796 	}
3797 }
3798 
handleModeEditKey(QKeyEvent * k,bool & keyRepeat)3799 void PageItem_TextFrame::handleModeEditKey(QKeyEvent *k, bool& keyRepeat)
3800 {
3801 	if (frameUnderflows())
3802 	{
3803 		itemText.setCursorPosition( itemText.length() );
3804 		PageItem * jumpFrame = frameTextEnd();
3805 		if (jumpFrame)
3806 		{
3807 			m_Doc->view()->deselectItems(true);
3808 			m_Doc->scMW()->selectItemsFromOutlines(jumpFrame);
3809 			m_Doc->scMW()->setTBvals(jumpFrame);
3810 			jumpFrame->update();
3811 			update();
3812 			switch (k->key())
3813 			{
3814 			case Qt::Key_PageDown:
3815 			case Qt::Key_PageUp:
3816 			case Qt::Key_End:
3817 			case Qt::Key_Home:
3818 			case Qt::Key_Right:
3819 			case Qt::Key_Left:
3820 			case Qt::Key_Up:
3821 			case Qt::Key_Down:
3822 			case Qt::Key_Delete:
3823 			case Qt::Key_Backspace:
3824 				break;
3825 			default:
3826 				jumpFrame->handleModeEditKey(k, keyRepeat);
3827 				jumpFrame->layout();
3828 				break;
3829 				}
3830 			return;
3831 		}
3832 	}
3833 	int oldPos = itemText.cursorPosition(); // 15-mar-2004 jjsa for cursor movement with Shift + Arrow key
3834 	int kk = k->key();
3835 	QString uc = k->text();
3836 #ifdef Q_OS_HAIKU
3837 	if (kk == Qt::Key_Return)
3838 		uc = "\r";
3839 #endif
3840 	int as = uc[0].unicode();
3841 	QString Tcha, Twort;
3842 	uint Tcoun;
3843 	int len, pos;
3844 	int keyModifiers=0;
3845 	Qt::KeyboardModifiers buttonModifiers = k->modifiers();
3846 	if (k->modifiers() & Qt::ShiftModifier)
3847 		keyModifiers |= Qt::SHIFT;
3848 	if (k->modifiers() & Qt::ControlModifier)
3849 		keyModifiers |= Qt::CTRL;
3850 	if (k->modifiers() & Qt::AltModifier)
3851 		keyModifiers |= Qt::ALT;
3852 
3853 	ScribusView* view = m_Doc->view();
3854 
3855 	//<< ISO 14755
3856 	//Check if we are trying to enter Unicode sequence mode first
3857 	if (view->m_ScMW->actionManager->compareKeySeqToShortcut(k->key(), buttonModifiers, "specialUnicodeSequenceBegin"))
3858 	{
3859 		unicodeTextEditMode = true;
3860 		unicodeInputCount = 0;
3861 		unicodeInputString = "";
3862 		keyRepeat = false;
3863 		return;
3864 	}
3865 	//>>
3866 
3867 
3868 	switch (kk)
3869 	{
3870 	case Qt::Key_PageDown:
3871 	case Qt::Key_PageUp:
3872 	case Qt::Key_End:
3873 	case Qt::Key_Home:
3874 	case Qt::Key_Right:
3875 	case Qt::Key_Left:
3876 	case Qt::Key_Up:
3877 	case Qt::Key_Down:
3878 		if ( (buttonModifiers & Qt::ShiftModifier) == 0 )
3879 			deselectAll();
3880 		if (UndoManager::undoEnabled())
3881 		{
3882 			SimpleState *ss = dynamic_cast<SimpleState*>(undoManager->getLastUndo());
3883 			if (ss)
3884 				ss->set("ETEA", QString(""));
3885 			else
3886 			{
3887 				TransactionState *ts = dynamic_cast<TransactionState*>(undoManager->getLastUndo());
3888 				if (ts)
3889 					ss = dynamic_cast<SimpleState*>(ts->last());
3890 				if (ss)
3891 					ss->set("ETEA", QString(""));
3892 			}
3893 		}
3894 	}
3895 
3896 	if (unicodeTextEditMode)
3897 	{
3898 		int conv = 0;
3899 		bool ok = false;
3900 		unicodeInputString += uc;
3901 		conv = unicodeInputString.toInt(&ok, 16);
3902 		if (!ok)
3903 		{
3904 			unicodeTextEditMode = false;
3905 			unicodeInputCount = 0;
3906 			unicodeInputString = "";
3907 			keyRepeat = false;
3908 			return;
3909 		}
3910 		unicodeInputCount++;
3911 		if (unicodeInputCount == 4)
3912 		{
3913 			unicodeTextEditMode = false;
3914 			unicodeInputCount = 0;
3915 			unicodeInputString = "";
3916 			if (ok)
3917 			{
3918 				UndoTransaction trans;
3919 				if (UndoManager::undoEnabled())
3920 					trans = undoManager->beginTransaction(Um::Selection,Um::ITextFrame,Um::InsertText,"",Um::IDelete);
3921 				if (itemText.hasSelection())
3922 					deleteSelectedTextFromFrame();
3923 				if (conv < 31)
3924 					conv = 32;
3925 				if (UndoManager::undoEnabled())
3926 				{
3927 					ScItemState<ParagraphStyle> *ip = nullptr;
3928 					SimpleState *ss = dynamic_cast<SimpleState*>(undoManager->getLastUndo());
3929 					UndoObject *undoTarget = this;
3930 					int cursorPos = itemText.cursorPosition();
3931 					if (conv == SpecialChars::PARSEP.unicode())
3932 					{
3933 						ip = new ScItemState<ParagraphStyle>(Um::InsertText, "", Um::ICreate);
3934 						ip->set("INSERT_FRAMEPARA");
3935 						ip->set("ETEA", QString("insert_framepara"));
3936 						ip->set("START", cursorPos);
3937 						ip->setItem(itemText.paragraphStyle(cursorPos));
3938 						if (isNoteFrame())
3939 						{
3940 							undoTarget = dynamic_cast<UndoObject*>(m_Doc);
3941 							ip->set("noteframeName", getUName());
3942 						}
3943 						undoManager->action(undoTarget, ip);
3944 					}
3945 					else if (ss && (ss->get("ETEA") == "insert_frametext") && (ss->undoObject() == undoTarget))
3946 						ss->set("TEXT_STR", ss->get("TEXT_STR") + QString(QChar(conv)));
3947 					else {
3948 						ss = new SimpleState(Um::InsertText, "", Um::ICreate);
3949 						ss->set("INSERT_FRAMETEXT");
3950 						ss->set("ETEA", QString("insert_frametext"));
3951 						ss->set("TEXT_STR", QString(QChar(conv)));
3952 						ss->set("START", itemText.cursorPosition());
3953 						if (isNoteFrame())
3954 						{
3955 							undoTarget = dynamic_cast<UndoObject*>(m_Doc);
3956 							ss->set("noteframeName", getUName());
3957 						}
3958 						undoManager->action(undoTarget, ss);
3959 					}
3960 				}
3961 				itemText.insertChars(QString(QChar(conv)), true);
3962 				if (trans)
3963 					trans.commit();
3964 //				Tinput = true;
3965 				m_Doc->scMW()->setTBvals(this);
3966 				if (isAutoNoteFrame() && m_Doc->notesChanged())
3967 				{
3968 					Q_ASSERT(asNoteFrame()->masterFrame());
3969 					asNoteFrame()->masterFrame()->invalid = true;
3970 				}
3971 				else
3972 					update();
3973 				keyRepeat = false;
3974 				return;
3975 			}
3976 		}
3977 		else
3978 		{
3979 			keyRepeat = false;
3980 			return;
3981 		}
3982 	}
3983 
3984 	int oldLast = lastInFrame();
3985 	switch (kk)
3986 	{
3987 	case Qt::Key_Home:
3988 		// go to begin of line
3989 		if ( (pos = itemText.cursorPosition()) == firstInFrame() )
3990 			break; // at begin of frame
3991 		len = lastInFrame();
3992 		if ( pos >= len )
3993 			pos = len - 1;
3994 		if ( (buttonModifiers & Qt::ControlModifier) == 0 )
3995 		{
3996 			pos = textLayout.startOfLine(pos);
3997 		}
3998 		else
3999 		{
4000 			//Control Home for start of frame text
4001 			pos = textLayout.startOfFrame();
4002 		}
4003 		itemText.setCursorPosition(pos);
4004 		if ( buttonModifiers & Qt::ShiftModifier )
4005 			ExpandSel(oldPos);
4006 //		if ( this->itemText.lengthOfSelection() > 0 )
4007 //			view->RefreshItem(this);
4008 		m_Doc->scMW()->setTBvals(this);
4009 		break;
4010 	case Qt::Key_End:
4011 		// go to end of line
4012 		len = lastInFrame();
4013 		if ( itemText.cursorPosition() >= (len + 1))
4014 			break; // at end of frame
4015 		if ( (buttonModifiers & Qt::ControlModifier) == 0 )
4016 		{
4017 			itemText.setCursorPosition( textLayout.endOfLine(itemText.cursorPosition()) );
4018 		}
4019 		else
4020 		{
4021 			//Control End for end of frame text
4022 			itemText.setCursorPosition( textLayout.endOfFrame() );
4023 		}
4024 		if ( buttonModifiers & Qt::ShiftModifier )
4025 			ExpandSel(oldPos);
4026 //		if ( this->itemText.lengthOfSelection() > 0 )
4027 //			view->RefreshItem(this);
4028 		m_Doc->scMW()->setTBvals(this);
4029 		break;
4030 	case Qt::Key_Down:
4031 		if (buttonModifiers & Qt::ControlModifier)
4032 		{
4033 			// go to end of paragraph
4034 			len = itemText.length();
4035 			itemText.setCursorPosition(itemText.nextParagraph(itemText.cursorPosition()));
4036 			if ( buttonModifiers & Qt::ShiftModifier )
4037 				ExpandSel(oldPos);
4038 		}
4039 		else
4040 		{
4041 			if (itemText.cursorPosition() <= lastInFrame())
4042 			{
4043 				itemText.setCursorPosition( textLayout.nextLine(itemText.cursorPosition()) );
4044 				if ( buttonModifiers & Qt::ShiftModifier )
4045 				{
4046 					if ( buttonModifiers & Qt::AltModifier )
4047 						itemText.setCursorPosition (lastInFrame() + 1);
4048 					ExpandSel(oldPos);
4049 				}
4050 				else
4051 					if ((textLayout.lines() > 0) && (oldPos >= textLayout.line(textLayout.lines() - 1)->firstChar()) && (itemText.cursorPosition() >= lastInFrame()) && (m_nextBox != nullptr))
4052 					{
4053 						if (m_nextBox->frameDisplays(itemText.cursorPosition()))
4054 						{
4055 							view->deselectItems(true);
4056 							// we position the cursor at the beginning of the next frame
4057 							// TODO position at the right place in next frame
4058 							m_Doc->scMW()->selectItemsFromOutlines(m_nextBox);
4059 						}
4060 					}
4061 			}
4062 			else
4063 			{
4064 				if (m_nextBox != nullptr)
4065 				{
4066 					if (m_nextBox->frameDisplays(lastInFrame() + 1))
4067 					{
4068 						view->deselectItems(true);
4069 						m_Doc->scMW()->selectItemsFromOutlines(m_nextBox);
4070 					}
4071 				}
4072 			}
4073 		}
4074 //		if ( this->itemText.lengthOfSelection() > 0 )
4075 //			view->RefreshItem(this);
4076 		m_Doc->scMW()->setTBvals(this);
4077 		break;
4078 	case Qt::Key_Up:
4079 		if (buttonModifiers & Qt::ControlModifier)
4080 		{
4081 			if ( (pos = itemText.cursorPosition()) == firstInFrame() )
4082 				break; // at begin of frame
4083 			len = itemText.length();
4084 			itemText.setCursorPosition( itemText.prevParagraph(itemText.cursorPosition()) );
4085 			if ( buttonModifiers & Qt::ShiftModifier )
4086 				ExpandSel(oldPos);
4087 		}
4088 		else
4089 		{
4090 			if (itemText.cursorPosition() > firstInFrame())
4091 			{
4092 				if (itemText.cursorPosition() > lastInFrame() || itemText.cursorPosition() >= itemText.length())
4093 					itemText.setCursorPosition(lastInFrame());
4094 				itemText.setCursorPosition( textLayout.prevLine(itemText.cursorPosition()) );
4095 				if ( buttonModifiers & Qt::ShiftModifier )
4096 				{
4097 					if ( buttonModifiers & Qt::AltModifier )
4098 						itemText.setCursorPosition( firstInFrame() );
4099 					ExpandSel(oldPos);
4100 				}
4101 				else
4102 					if ((textLayout.lines() > 0) && (oldPos <= textLayout.line(0)->lastChar()) && (itemText.cursorPosition()  == firstInFrame()) && (m_backBox != nullptr))
4103 					{
4104 						view->deselectItems(true);
4105 						// TODO position at the right place in previous frame
4106 						m_backBox->itemText.setCursorPosition( m_backBox->lastInFrame() );
4107 						m_Doc->scMW()->selectItemsFromOutlines(m_backBox);
4108 					}
4109 			}
4110 			else
4111 			{
4112 				itemText.setCursorPosition( firstInFrame() );
4113 				if (m_backBox != nullptr)
4114 				{
4115 					view->deselectItems(true);
4116 					m_backBox->itemText.setCursorPosition( m_backBox->lastInFrame() );
4117 					m_Doc->scMW()->selectItemsFromOutlines(m_backBox);
4118 				}
4119 			}
4120 		}
4121 //		if ( this->itemText.lengthOfSelection() > 0 )
4122 //			view->RefreshItem(this);
4123 		m_Doc->scMW()->setTBvals(this);
4124 		break;
4125 	case Qt::Key_PageUp:
4126 		if (itemText.cursorPosition() == firstInFrame() && m_backBox != nullptr)
4127 		{
4128 			view->deselectItems(true);
4129 			m_backBox->itemText.setCursorPosition( m_backBox->firstInFrame() );
4130 			m_Doc->scMW()->selectItemsFromOutlines(m_backBox);
4131 			//currItem = currItem->BackBox;
4132 		}
4133 		else
4134 			itemText.setCursorPosition( textLayout.startOfFrame() );
4135 		if ( buttonModifiers & Qt::ShiftModifier )
4136 			ExpandSel(oldPos);
4137 		m_Doc->scMW()->setTBvals(this);
4138 		break;
4139 	case Qt::Key_PageDown:
4140 		if (!frameDisplays(itemText.length() - 1) && itemText.cursorPosition() >= lastInFrame() && m_nextBox != nullptr)
4141 		{
4142 			view->deselectItems(true);
4143 			itemText.setCursorPosition( m_nextBox->lastInFrame() );
4144 			m_Doc->scMW()->selectItemsFromOutlines(m_nextBox);
4145 			//currItem = currItem->BackBox;
4146 		}
4147 		else
4148 			itemText.setCursorPosition( textLayout.endOfFrame() );
4149 		if ( buttonModifiers & Qt::ShiftModifier )
4150 			ExpandSel(oldPos);
4151 		m_Doc->scMW()->setTBvals(this);
4152 		break;
4153 	case Qt::Key_Left:
4154 		if ( buttonModifiers & Qt::ControlModifier )
4155 		{
4156 			itemText.moveCursorWordLeft();
4157 			if ( buttonModifiers & Qt::ShiftModifier )
4158 				ExpandSel(oldPos);
4159 		}
4160 		else if ( buttonModifiers & Qt::ShiftModifier )
4161 		{
4162 			itemText.moveCursorLeft();
4163 			ExpandSel(oldPos);
4164 		}
4165 		else
4166 		{
4167 			itemText.moveCursorLeft();
4168 			if (itemText.cursorPosition() < firstInFrame())
4169 			{
4170 				itemText.setCursorPosition( firstInFrame() );
4171 				if (m_backBox != nullptr)
4172 				{
4173 					view->deselectItems(true);
4174 					m_backBox->itemText.setCursorPosition( m_backBox->lastInFrame() );
4175 					m_Doc->scMW()->selectItemsFromOutlines(m_backBox);
4176 					//currItem = currItem->BackBox;
4177 				}
4178 			}
4179 		}
4180 
4181 		while ((itemText.cursorPosition() > 1) && (itemText.flags(itemText.cursorPosition() - 1) & ScLayout_SuppressSpace))
4182 		{
4183 			itemText.moveCursorLeft();
4184 			if (itemText.cursorPosition() == 0)
4185 				break;
4186 		}
4187 
4188 //		if ( itemText.lengthOfSelection() > 0 )
4189 //			view->RefreshItem(this);
4190 		m_Doc->scMW()->setTBvals(this);
4191 		break;
4192 	case Qt::Key_Right:
4193 		if ( buttonModifiers & Qt::ControlModifier )
4194 		{
4195 			itemText.moveCursorWordRight();
4196 			if ( buttonModifiers & Qt::ShiftModifier )
4197 				ExpandSel(oldPos);
4198 		}
4199 		else if ( buttonModifiers & Qt::ShiftModifier )
4200 		{
4201 			itemText.moveCursorRight();
4202 			ExpandSel(oldPos);
4203 		}
4204 		else
4205 		{
4206 			itemText.moveCursorRight(); // new position within text ?
4207 			if (itemText.cursorPosition() > lastInFrame())
4208 			{
4209 //				--CPos;
4210 				itemText.setCursorPosition(lastInFrame() + 1);
4211 				if (m_nextBox != nullptr)
4212 				{
4213 					if (m_nextBox->frameDisplays(itemText.cursorPosition()))
4214 					{
4215 						view->deselectItems(true);
4216 						m_Doc->scMW()->selectItemsFromOutlines(m_nextBox);
4217 						//currItem = currItem->NextBox;
4218 					}
4219 				}
4220 			}
4221 		}
4222 // 		if ( itemText.lengthOfSelection() > 0 )
4223 // 			update();
4224 		m_Doc->scMW()->setTBvals(this);
4225 		break;
4226 	case Qt::Key_Delete:
4227 		if (itemText.cursorPosition() == itemText.length())
4228 		{
4229 			if (itemText.hasSelection())
4230 			{
4231 				deleteSelectedTextFromFrame();
4232 				m_Doc->scMW()->setTBvals(this);
4233 				if (isAutoNoteFrame() && asNoteFrame()->notesList().isEmpty())
4234 				{
4235 					if (!asNoteFrame()->isEndNotesFrame())
4236 					{
4237 						Q_ASSERT(asNoteFrame()->masterFrame());
4238 						asNoteFrame()->masterFrame()->invalid = true;
4239 					}
4240 				}
4241 				else
4242 					update();
4243 //				view->RefreshItem(this);
4244 			}
4245 			keyRepeat = false;
4246 			return;
4247 		}
4248 		if (itemText.length() == 0)
4249 		{
4250 			keyRepeat = false;
4251 			return;
4252 		}
4253 		if (!itemText.hasSelection())
4254 		{
4255 			int pos1 = itemText.cursorPosition();
4256 			itemText.moveCursorForward();
4257 			int pos2 = itemText.cursorPosition();
4258 			itemText.select(pos1, pos2 - pos1, true);
4259 		}
4260 		deleteSelectedTextFromFrame();
4261 		if (isAutoNoteFrame() && asNoteFrame()->notesList().isEmpty())
4262 		{
4263 			if (!asNoteFrame()->isEndNotesFrame())
4264 			{
4265 				Q_ASSERT(asNoteFrame()->masterFrame());
4266 				asNoteFrame()->masterFrame()->invalid = true;
4267 			}
4268 		}
4269 		else
4270 		{
4271 			layout();
4272 			if (oldLast != lastInFrame() && m_nextBox != nullptr && m_nextBox->invalid)
4273 				m_nextBox->updateLayout();
4274 		}
4275 //		Tinput = false;
4276 //		if ((cr == QChar(13)) && (itemText.length() != 0))
4277 //		{
4278 //			m_Doc->chAbStyle(this, findParagraphStyle(m_Doc, itemText.paragraphStyle(qMax(itemText.cursorPosition() - 1,0))));
4279 //			Tinput = false;
4280 //		}
4281 		m_Doc->scMW()->setTBvals(this);
4282 //		view->RefreshItem(this);
4283 		break;
4284 	case Qt::Key_Backspace:
4285 		if (itemText.cursorPosition() == 0)
4286 		{
4287 			if (itemText.hasSelection())
4288 			{
4289 				deleteSelectedTextFromFrame();
4290 				m_Doc->scMW()->setTBvals(this);
4291 				if (isAutoNoteFrame() && asNoteFrame()->notesList().isEmpty())
4292 				{
4293 					if (!asNoteFrame()->isEndNotesFrame())
4294 					{
4295 						Q_ASSERT(asNoteFrame()->masterFrame());
4296 						asNoteFrame()->masterFrame()->invalid = true;
4297 					}
4298 				}
4299 				else
4300 					update();
4301 			}
4302 			break;
4303 		}
4304 		if (itemText.length() == 0)
4305 			break;
4306 		if (!itemText.hasSelection())
4307 		{
4308 			itemText.setCursorPosition(-1, true);
4309 			itemText.select(itemText.cursorPosition(), 1, true);
4310 		}
4311 		deleteSelectedTextFromFrame();
4312 //		Tinput = false;
4313 //		if ((cr == QChar(13)) && (itemText.length() != 0))
4314 //		{
4315 //			m_Doc->chAbStyle(this, findParagraphStyle(m_Doc, itemText.paragraphStyle(qMax(CPos - 1,0))));
4316 //			Tinput = false;
4317 //		}
4318 		if (isAutoNoteFrame() && asNoteFrame()->notesList().isEmpty())
4319 		{
4320 			if (!asNoteFrame()->isEndNotesFrame())
4321 			{
4322 				Q_ASSERT(asNoteFrame()->masterFrame());
4323 				asNoteFrame()->masterFrame()->invalid = true;
4324 			}
4325 		}
4326 		else
4327 		{
4328 			layout();
4329 			if (oldLast != lastInFrame() && m_nextBox != nullptr && m_nextBox->invalid)
4330 				m_nextBox->updateLayout();
4331 		}
4332 		if (itemText.cursorPosition() < firstInFrame())
4333 		{
4334 			itemText.setCursorPosition( firstInFrame() );
4335 			if (m_backBox != nullptr)
4336 			{
4337 				view->deselectItems(true);
4338 				if (m_backBox->invalid)
4339 					m_backBox->updateLayout();
4340 				itemText.setCursorPosition( m_backBox->lastInFrame() );
4341 				m_Doc->scMW()->selectItemsFromOutlines(m_backBox);
4342 				//currItem = currItem->BackBox;
4343 			}
4344 		}
4345 		m_Doc->scMW()->setTBvals(this);
4346 //		update();
4347 //		view->RefreshItem(this);
4348 		break;
4349 	default:
4350 		if (isNoteFrame() && itemText.cursorPosition() == 0)
4351 			break; //avoid inserting chars before first note mark
4352 		bool doUpdate = false;
4353 		UndoTransaction activeTransaction;
4354 		if (itemText.hasSelection()) //(kk < 0x1000)
4355 		{
4356 			bool x11Hack=false;
4357 			bool controlCharHack=false;
4358 			if ((k->text().size() == 1))
4359 			{
4360 				ushort uni = k->text().at(0).unicode();
4361 				controlCharHack = ((uni < 32) && (uni != SpecialChars::TAB.unicode()));
4362 			}
4363 #if defined (Q_OS_LINUX)
4364 			if ((k->text().size()==1) && (k->modifiers() & Qt::ShiftModifier) && (k->modifiers() & Qt::ControlModifier) && (k->nativeVirtualKey() < 1000))
4365 				x11Hack=true;
4366 #endif
4367 			if (!controlCharHack && !x11Hack && !k->text().isEmpty())
4368 			{
4369 				if (UndoManager::undoEnabled())
4370 					activeTransaction = undoManager->beginTransaction(Um::Selection, Um::IGroup, Um::ReplaceText, "", Um::IDelete);
4371 
4372 				deleteSelectedTextFromFrame();
4373 				doUpdate = true;
4374 			}
4375 			/*
4376 			qDebug()<<"Key:"<<k->key();
4377 			qDebug()<<"Text:"<<k->text();
4378 			qDebug()<<"Size:"<<k->text().size();
4379 			qDebug()<<"Modifiers:"<<k->modifiers();
4380 			qDebug()<<"Native Modifiers:"<<k->nativeModifiers();
4381 			qDebug()<<"Native Scan Code:"<<k->nativeScanCode();
4382 			qDebug()<<"Native Virtual Key:"<<k->nativeVirtualKey();
4383 			*/
4384 		}
4385 		//if ((kk == Qt::Key_Tab) || ((kk == Qt::Key_Return) && (buttonState & Qt::ShiftButton)))
4386 		if (kk == Qt::Key_Tab)
4387 		{
4388 			if (UndoManager::undoEnabled())
4389 			{
4390 				SimpleState *ss = dynamic_cast<SimpleState*>(undoManager->getLastUndo());
4391 				UndoObject *undoTarget = this;
4392 				if (ss && (ss->get("ETEA") == "insert_frametext") && (ss->undoObject() == undoTarget))
4393 					ss->set("TEXT_STR", ss->get("TEXT_STR") + QString(SpecialChars::TAB));
4394 				else
4395 				{
4396 					ss = new SimpleState(Um::InsertText, "", Um::ICreate);
4397 					ss->set("INSERT_FRAMETEXT");
4398 					ss->set("ETEA", QString("insert_frametext"));
4399 					ss->set("TEXT_STR", QString(SpecialChars::TAB));
4400 					ss->set("START", itemText.cursorPosition());
4401 					UndoObject * undoTarget = this;
4402 					if (isNoteFrame())
4403 					{
4404 						undoTarget = m_Doc;
4405 						ss->set("noteframeName", getUName());
4406 					}
4407 					undoManager->action(undoTarget, ss);
4408 				}
4409 			}
4410 			itemText.insertChars(QString(SpecialChars::TAB), true);
4411 //			Tinput = true;
4412 //			view->RefreshItem(this);
4413 			doUpdate = true;
4414 		}
4415 		else if (uc[0] > QChar(31) || (as == 13) || (as == 30))
4416 		{
4417 			if (UndoManager::undoEnabled())
4418 			{
4419 				ScItemState<ParagraphStyle> *ip = nullptr;
4420 				SimpleState *ss = dynamic_cast<SimpleState*>(undoManager->getLastUndo());
4421 				UndoObject *undoTarget = this;
4422 				int cursorPos = itemText.cursorPosition();
4423 				if (uc[0] == SpecialChars::PARSEP)
4424 				{
4425 					ip = new ScItemState<ParagraphStyle>(Um::InsertText, "", Um::ICreate);
4426 					ip->set("INSERT_FRAMEPARA");
4427 					ip->set("ETEA", QString("insert_framepara"));
4428 					ip->set("START", cursorPos);
4429 					ip->setItem(itemText.paragraphStyle(cursorPos));
4430 					if (isNoteFrame())
4431 					{
4432 						undoTarget = dynamic_cast<UndoObject*>(m_Doc);
4433 						ip->set("noteframeName", getUName());
4434 					}
4435 					undoManager->action(undoTarget, ip);
4436 				}
4437 				else if (ss && (ss->get("ETEA") == "insert_frametext") && (ss->undoObject() == undoTarget))
4438 					ss->set("TEXT_STR", ss->get("TEXT_STR") + uc);
4439 				else
4440 				{
4441 					ss = new SimpleState(Um::InsertText, "", Um::ICreate);
4442 					ss->set("INSERT_FRAMETEXT");
4443 					ss->set("ETEA", QString("insert_frametext"));
4444 					ss->set("TEXT_STR", uc);
4445 					ss->set("START", cursorPos);
4446 					if (isNoteFrame())
4447 					{
4448 						undoTarget = dynamic_cast<UndoObject*>(m_Doc);
4449 						ss->set("noteframeName", getUName());
4450 					}
4451 					undoManager->action(undoTarget, ss);
4452 				}
4453 			}
4454 			itemText.insertChars(uc, true);
4455 			if ((m_Doc->docHyphenator->AutoCheck) && (itemText.cursorPosition() > 1))
4456 			{
4457 				Twort = "";
4458 				Tcoun = 0;
4459 				for (int hych = itemText.cursorPosition() - 1; hych > -1; hych--)
4460 				{
4461 					Tcha = itemText.text(hych,1);
4462 					if (Tcha[0] == ' ')
4463 					{
4464 						Tcoun = hych + 1;
4465 						break;
4466 					}
4467 					Twort.prepend(Tcha);
4468 				}
4469 				if (!Twort.isEmpty())
4470 				{
4471 					m_Doc->docHyphenator->slotHyphenateWord(this, Twort, Tcoun);
4472 				}
4473 			}
4474 			invalid = true;
4475 //			Tinput = true;
4476 //			view->RefreshItem(this);
4477 			doUpdate = true;
4478 		}
4479 		if (doUpdate)
4480 		{
4481 			if (activeTransaction)
4482 			{
4483 				activeTransaction.commit();
4484 			}
4485 			// update layout immediately, we need MaxChars to be correct to detect
4486 			// if we need to move to next frame or not
4487 			if (isAutoNoteFrame() && asNoteFrame()->notesList().isEmpty())
4488 			{
4489 				if (!asNoteFrame()->isEndNotesFrame())
4490 				{
4491 					Q_ASSERT(asNoteFrame()->masterFrame());
4492 					asNoteFrame()->masterFrame()->invalid = true;
4493 				}
4494 			}
4495 			else
4496 				update();
4497 			if (oldLast != lastInFrame() && m_nextBox != nullptr && m_nextBox->invalid)
4498 				m_nextBox->updateLayout();
4499 		}
4500 		//check if cursor need to jump to next linked frame
4501 		//but not for notes frames can`t be updated as may disapper during update
4502 		if ((itemText.cursorPosition() > lastInFrame() + 1) && (lastInFrame() < (itemText.length() - 2)) && m_nextBox != nullptr)
4503 		{
4504 			view->deselectItems(true);
4505 			m_nextBox->update();
4506 			m_Doc->scMW()->selectItemsFromOutlines(m_nextBox);
4507 		}
4508 		break;
4509 	}
4510 // 	update();
4511 //	view->slotDoCurs(true);
4512 	if ((kk == Qt::Key_Left) || (kk == Qt::Key_Right) || (kk == Qt::Key_Up) || (kk == Qt::Key_Down))
4513 		keyRepeat = false;
4514 }
4515 
deleteSelectedTextFromFrame()4516 void PageItem_TextFrame::deleteSelectedTextFromFrame(/*bool findNotes*/)
4517 {
4518 	if (itemText.length() <= 0)
4519 		return;
4520 	if (!itemText.hasSelection())
4521 	{
4522 		itemText.select(itemText.cursorPosition(), 1);
4523 		HasSel = true;
4524 	}
4525 	int start = itemText.startOfSelection();
4526 	int stop = itemText.endOfSelection();
4527 	int savedStart = itemText.startOfSelection();
4528 	int savedStop = itemText.endOfSelection();
4529 	int marksNum = 0;
4530 	if (UndoManager::undoEnabled())
4531 	{
4532 		int lastPos = start;
4533 		CharStyle lastParent = itemText.charStyle(start);
4534 		UndoState* state = undoManager->getLastUndo();
4535 		ScItemState<ParagraphStyle> *ip = nullptr;
4536 		ScItemState<CharStyle> *is = nullptr;
4537 		TransactionState *ts = nullptr;
4538 		bool added = false;
4539 		bool lastIsDelete = false;
4540 		while (state && state->isTransaction()) {
4541 			ts = dynamic_cast<TransactionState*>(state);
4542 			is = dynamic_cast<ScItemState<CharStyle>*>(ts->last());
4543 			state = ts->last();
4544 		}
4545 		QString eteaString = (m_Doc->appMode == modeEdit) ? "delete_frametext" : "";
4546 		UndoTransaction trans = undoManager->beginTransaction(Um::Selection,Um::IDelete,Um::Delete,"",Um::IDelete);
4547 
4548 		//find and delete notes and marks in selected text
4549 		QList<QPair<TextNote*, int> > notes2DEL;
4550 		if (isNoteFrame()/* && findNotes*/)
4551 		{
4552 			//find and delete notes
4553 			//if marks are in notes then they will be deleted further while note is physically deleted
4554 			for (int i = start; i < stop; ++i)
4555 			{
4556 				if (i == itemText.length())
4557 					break;
4558 				Mark* mark = itemText.mark(i);
4559 				if (itemText.hasMark(i) && mark->isType(MARKNoteFrameType))
4560 					notes2DEL.append(QPair<TextNote*, int>(mark->getNotePtr(), i));
4561 			}
4562 		}
4563 		else
4564 		{
4565 			//delete marks from selected text (with undo)
4566 			marksNum = removeMarksFromText(true);
4567 			stop -= marksNum;
4568 		}
4569 		//delete text
4570 		for (int i = start; i <= stop; ++i)
4571 		{
4572 			Mark* mark = i < itemText.length() && itemText.hasMark(i) ? itemText.mark(i) : nullptr;
4573 			const CharStyle& curParent = itemText.charStyle(i);
4574 			bool needParaAction = ((i < stop) && (itemText.text(i) == SpecialChars::PARSEP));
4575 			if (i == stop || !curParent.equiv(lastParent) || (mark && mark->isType(MARKNoteFrameType)) || needParaAction)
4576 			{
4577 				added = false;
4578 				lastIsDelete = false;
4579 				if (is && ts && is->get("ETEA") == "delete_frametext" && lastPos < is->getInt("START"))
4580 				{
4581 					int oldStart = is->getInt("START");
4582 					if (is->getItem().equiv(lastParent) && (i - lastPos > 0) && (start + i - lastPos == oldStart))
4583 					{
4584 						is->set("START", start);
4585 						is->set("TEXT_STR", itemText.text(lastPos, i - lastPos) + is->get("TEXT_STR"));
4586 						added = true;
4587 						lastIsDelete = true;
4588 					}
4589 				}
4590 				else if (is && ts && is->get("ETEA") == "delete_frametext"  && lastPos >= is->getInt("START"))
4591 				{
4592 					int oldStart = is->getInt("START");
4593 					if (is->getItem().equiv(lastParent) && (i - lastPos > 0) && (oldStart == start))
4594 					{
4595 						is->set("TEXT_STR", is->get("TEXT_STR") + itemText.text(lastPos, i - lastPos));
4596 						added = true;
4597 						lastIsDelete = true;
4598 					}
4599 				}
4600 				if (!added)
4601 				{
4602 					UndoObject * undoTarget = this;
4603 					is = nullptr;
4604 					if (i - lastPos > 0)
4605 					{
4606 						is = new ScItemState<CharStyle>(Um::DeleteText, "", Um::IDelete);
4607 						is->set("DELETE_FRAMETEXT");
4608 						is->set("ETEA", eteaString);
4609 						is->set("TEXT_STR", itemText.text(lastPos, i - lastPos));
4610 						is->set("START", start);
4611 						is->setItem(lastParent);
4612 					}
4613 					//delete selected notes from notes frame
4614 					if (isNoteFrame())
4615 					{
4616 						undoTarget = m_Doc; //undo target is doc for notes as after deleting last note notesframe can be deleted
4617 						if (is)
4618 							is->set("noteframeName", getUName());
4619 						//remove marks from notes
4620 						for (int ii = notes2DEL.count() - 1; ii >= 0; --ii)
4621 						{
4622 							TextNote* note = notes2DEL.at(ii).first;
4623 							Q_ASSERT(note != nullptr);
4624 							if (note->textLen <= 0)
4625 								continue;
4626 							int oldTextLen = itemText.length();
4627 							itemText.deselectAll();
4628 							itemText.select(notes2DEL.at(ii).second + 1, note->textLen);
4629 							removeMarksFromText(true);
4630 							marksNum += oldTextLen - itemText.length();
4631 						}
4632 						asNoteFrame()->updateNotesText();
4633 						for (int ii = notes2DEL.count() - 1; ii >= 0; --ii)
4634 						{
4635 							TextNote* note = notes2DEL.at(ii).first;
4636 							Q_ASSERT(note != nullptr);
4637 							int oldTextLen = itemText.length();
4638 							m_Doc->setUndoDelNote(note);
4639 							if (note->isEndNote())
4640 								m_Doc->flag_updateEndNotes = true;
4641 							m_Doc->deleteNote(note);
4642 							marksNum += oldTextLen - itemText.length();
4643 						}
4644 						if (is)
4645 						{
4646 							if (!ts || !lastIsDelete) {
4647 								undoManager->action(undoTarget, is);
4648 								ts = nullptr;
4649 							}
4650 							else
4651 								ts->pushBack(undoTarget, is);
4652 						}
4653 						break;
4654 					}
4655 					if (is)
4656 					{
4657 						if (!ts || !lastIsDelete) {
4658 							undoManager->action(undoTarget, is);
4659 							ts = nullptr;
4660 						}
4661 						else
4662 							ts->pushBack(undoTarget, is);
4663 					}
4664 				}
4665 				lastPos = i;
4666 				lastParent = curParent;
4667 			}
4668 			if (needParaAction)
4669 			{
4670 				UndoObject * undoTarget = this;
4671 				if (isNoteFrame())
4672 					undoTarget = m_Doc;
4673 				ip = new ScItemState<ParagraphStyle>(Um::DeleteText, "", Um::IDelete);
4674 				ip->set("DELETE_FRAMEPARA");
4675 				ip->set("ETEA", QString("delete_framepara"));
4676 				ip->set("START", start);
4677 				ip->setItem(itemText.paragraphStyle(i));
4678 				lastPos = i + 1;
4679 				if (lastPos < itemText.length())
4680 					lastParent = itemText.charStyle(lastPos);
4681 				QString etea;
4682 				SimpleState* ss = ts ? dynamic_cast<SimpleState*>(ts->last()) : nullptr;
4683 				if (ss)
4684 					etea = ss->get("ETEA");
4685 				if (ts && ((etea == "delete_frametext") || (etea == "delete_framepara")))
4686 					ts->pushBack(undoTarget, ip);
4687 				else
4688 					undoManager->action(undoTarget, ip);
4689 				is = nullptr;
4690 				ts = nullptr;
4691 			}
4692 		}
4693 		if (trans)
4694 		{
4695 			trans.commit();
4696 		}
4697 	}
4698 	else //remove marks without undo
4699 		marksNum = removeMarksFromText(false);
4700 
4701 	// We have to use saved selection because mark removal may clear selection
4702 	// so that we cannot get it back from itemText
4703 	itemText.deselectAll();
4704 	itemText.setCursorPosition(savedStart);
4705 	itemText.select(savedStart, savedStop - savedStart - marksNum);
4706 	itemText.removeSelection();
4707 	HasSel = false;
4708 //	m_Doc->updateFrameItems();
4709 	m_Doc->scMW()->setCopyCutEnabled(false);
4710 }
4711 
checkKeyIsShortcut(QKeyEvent * k)4712 bool PageItem_TextFrame::checkKeyIsShortcut(QKeyEvent *k)
4713 {
4714 	QMap<QString, Keys> keyMap=PrefsManager::instance().appPrefs.keyShortcutPrefs.KeyActions;
4715 
4716 	bool ret = false;
4717 	int keyCode =0;
4718 	if (k->modifiers() & Qt::ShiftModifier)
4719 		keyCode |= Qt::SHIFT;
4720 	if (k->modifiers() & Qt::ControlModifier)
4721 		keyCode |= Qt::CTRL;
4722 	if (k->modifiers() & Qt::AltModifier)
4723 		keyCode |= Qt::ALT;
4724 	keyCode|=k->key();
4725 
4726 	QKeySequence key = QKeySequence(keyCode);
4727 	for (QMap<QString,Keys>::Iterator it=keyMap.begin(); it!=keyMap.end(); ++it)
4728 	{
4729 		if (key.matches(it.value().keySequence) != QKeySequence::NoMatch)
4730 		{
4731 			ret = true;
4732 			break;
4733 		}
4734 	}
4735 	return ret;
4736 
4737 }
4738 
updateBulletsNum()4739 void PageItem_TextFrame::updateBulletsNum()
4740 {
4741 	for (int index = 0; index < itemText.length(); ++index)
4742 	{
4743 		Mark* mark = itemText.mark(index);
4744 		if ((mark != nullptr) && (itemText.hasMark(index)))
4745 		{
4746 			mark->OwnPage = this->OwnPage;
4747 			//itemPtr and itemName set to this frame only if mark type is different than MARK2ItemType
4748 			if (!mark->isType(MARK2ItemType))
4749 			{
4750 				mark->setItemPtr(this);
4751 				mark->setItemName(itemName());
4752 			}
4753 
4754 			//anchors and indexes has no visible inserts in text
4755 			if (mark->isType(MARKAnchorType) || mark->isType(MARKIndexType))
4756 				continue;
4757 
4758 			//set note marker charstyle
4759 			if (mark->isNoteType())
4760 			{
4761 				TextNote* note = mark->getNotePtr();
4762 				if (note == nullptr)
4763 					continue;
4764 				mark->setItemPtr(this);
4765 				NotesStyle* nStyle = note->notesStyle();
4766 				Q_ASSERT(nStyle != nullptr);
4767 				QString chsName = nStyle->marksChStyle();
4768 				CharStyle currStyle(itemText.charStyle(index));
4769 				if (!chsName.isEmpty())
4770 				{
4771 					CharStyle marksStyle(m_Doc->charStyle(chsName));
4772 					if (!currStyle.equiv(marksStyle))
4773 					{
4774 						currStyle.setParent(chsName);
4775 						itemText.applyCharStyle(index, 1, currStyle);
4776 					}
4777 				}
4778 
4779 				StyleFlag s(itemText.charStyle(index).effects());
4780 				if (mark->isType(MARKNoteMasterType))
4781 				{
4782 					if (nStyle->isSuperscriptInMaster())
4783 						s |= ScStyle_Superscript;
4784 					else
4785 						s &= ~ScStyle_Superscript;
4786 				}
4787 				else
4788 				{
4789 					if (nStyle->isSuperscriptInNote())
4790 						s |= ScStyle_Superscript;
4791 					else
4792 						s &= ~ScStyle_Superscript;
4793 				}
4794 				if (s != itemText.charStyle(index).effects())
4795 				{
4796 					CharStyle haveSuperscript;
4797 					haveSuperscript.setFeatures(s.featureList());
4798 					itemText.applyCharStyle(index, 1, haveSuperscript);
4799 				}
4800 			}
4801 		}
4802 
4803 		bool bullet = false;
4804 		if (index == 0 || itemText.text(index - 1) == SpecialChars::PARSEP)
4805 		{
4806 			const ParagraphStyle& style = itemText.paragraphStyle(index);
4807 			if (style.hasBullet() || style.hasNum())
4808 			{
4809 				bullet = true;
4810 				if (mark == nullptr || !mark->isType(MARKBullNumType))
4811 				{
4812 					itemText.insertMark(new BulNumMark(), index);
4813 					index--;
4814 					continue;
4815 				}
4816 				if (style.hasBullet())
4817 					mark->setString(style.bulletStr());
4818 				else if (style.hasNum() && mark->getString().isEmpty())
4819 				{
4820 					mark->setString("?");
4821 				}
4822 			}
4823 		}
4824 
4825 		if (!bullet && mark && mark->isType(MARKBullNumType))
4826 		{
4827 			itemText.removeChars(index, 1);
4828 			index--;
4829 			continue;
4830 		}
4831 
4832 	}
4833 
4834 	m_Doc->updateNumbers();
4835 }
4836 
4837 
4838 // jjsa added on 15-mar-2004 expand / decrease selection
4839 
4840 // jjsa added on 14-mar-2004 text selection with pressed
4841 // shift button and <-, -> cursor keys
4842 // Parameters
4843 //   PageItem *currItem text item to be processed
4844 //   inc < 0 for left key > 0 for right key
4845 //  if value is +/-1 work on slection
4846 //  if value is +/-2 refresh if text under cursor is selected -- not used
4847 
ExpandSel(int oldPos)4848 void PageItem_TextFrame::ExpandSel(int oldPos)
4849 {
4850 	int start = itemText.startOfSelection();
4851 	// itemText.endOfSelection can return -1 in some case, which results in a crash
4852 	// when calling itemText.select(end, CPos - end).
4853 	// as I do not know where it is checked for negative value, just work around by
4854 	// preventing it to be less than 0 here.
4855 	int end = qMax(0,itemText.endOfSelection());
4856 
4857 	if (oldPos >= end && itemText.cursorPosition() < start)
4858 	{
4859 		itemText.deselectAll();
4860 		itemText.select(itemText.cursorPosition(), start - itemText.cursorPosition());
4861 	}
4862 	else if (oldPos <= start && itemText.cursorPosition() > end)
4863 	{
4864 		itemText.deselectAll();
4865 		itemText.select(end, itemText.cursorPosition() - end);
4866 	}
4867 	else
4868 	{
4869 		itemText.extendSelection(oldPos, itemText.cursorPosition());
4870 	}
4871 	HasSel = itemText.hasSelection();
4872 	m_Doc->scMW()->setCopyCutEnabled(HasSel);
4873 	cursorBiasBackward = (oldPos > itemText.cursorPosition());
4874 
4875 // 	layoutWeakLock = true;
4876 // 	update();
4877 	m_Doc->regionsChanged()->update(getBoundingRect());
4878 }
4879 
deselectAll()4880 void PageItem_TextFrame::deselectAll()
4881 {
4882 	if (itemText.hasSelection())
4883 	{
4884 		itemText.deselectAll();
4885 		PageItem *item = this;
4886 		while (item->prevInChain())
4887 			item = item->prevInChain();
4888 
4889 		while (item)
4890 		{
4891 			item->HasSel = false;
4892 			item = item->nextInChain();
4893 		}
4894 // 		update();
4895 		m_Doc->regionsChanged()->update(getBoundingRect());
4896 	}
4897 	//CB Replace with direct call for now //emit HasNoTextSel();
4898 	m_Doc->scMW()->setCopyCutEnabled(false);
4899 }
4900 
columnWidth()4901 double PageItem_TextFrame::columnWidth()
4902 {
4903 	double lineCorr;
4904 	if (lineColor() != CommonStrings::None)
4905 		lineCorr = m_lineWidth / 2.0;
4906 	else
4907 		lineCorr = 0;
4908 	return (m_width - (m_columnGap * (m_columns - 1)) - m_textDistanceMargins.left() - m_textDistanceMargins.right() - 2 * lineCorr) / m_columns;
4909 //	return (Width - (ColGap * (Cols - 1)) - m_textDistanceMargins.left() - m_textDistanceMargins.right() - lineCorr) / Cols;
4910 }
4911 
4912 /*
4913 void PageItem_TextFrame::drawOverflowMarker(ScPainter *p)
4914 {
4915 	qreal sideLength = 10 / qMax(p->zoomFactor(), 1.0);
4916 	qreal left  = Width  - sideLength;// / 2;
4917 	qreal right = left   + sideLength;
4918 	qreal top   = Height - sideLength;// * 1.5;
4919 	qreal bottom = top   + sideLength;
4920 
4921 	QColor color(PrefsManager::instance().appPrefs.displayPrefs.frameNormColor);
4922 	if ((isBookmark) || (m_isAnnotation))
4923 		color = PrefsManager::instance().appPrefs.displayPrefs.frameAnnotationColor;
4924 	if ((BackBox != nullptr) || (NextBox != nullptr))
4925 		color = PrefsManager::instance().appPrefs.displayPrefs.frameLinkColor;
4926 	if (m_Locked)
4927 		color = PrefsManager::instance().appPrefs.displayPrefs.frameLockColor;
4928 	if (m_Doc->m_Selection->containsItem(this))
4929 		color = Qt::red;
4930 
4931 	p->setPen(color, 0.5 / p->zoomFactor(), Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
4932 	p->setPenOpacity(1.0);
4933 	p->setBrush(Qt::white);
4934 	p->setBrushOpacity(1.0);
4935 	p->setFillMode(ScPainter::Solid);
4936 	p->drawRect(left, top, sideLength, sideLength);
4937 	p->drawLine(FPoint(left, top), FPoint(right, bottom));
4938 	p->drawLine(FPoint(left, bottom), FPoint(right, top));
4939 }
4940 */
4941 
drawColumnBorders(ScPainter * p)4942 void PageItem_TextFrame::drawColumnBorders(ScPainter *p)
4943 {
4944 	p->setPen(Qt::gray, 0, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
4945 	p->setPenOpacity(1.0);
4946 	p->setBrush(Qt::white);
4947 	p->setBrushOpacity(1.0);
4948 	p->setFillMode(ScPainter::Solid);
4949 	p->setupPolygon(&PoLine);
4950 	p->setClipPath();
4951 	double colWidth = columnWidth();
4952 	double colLeft=0;
4953 	int curCol=0;
4954 	double lineCorr=0;
4955 	if (lineColor() != CommonStrings::None)
4956 		lineCorr = m_lineWidth / 2.0;
4957 	if (m_textDistanceMargins.top() + lineCorr!=0.0)
4958 		p->drawSharpLine(FPoint(m_textDistanceMargins.left() + lineCorr, m_textDistanceMargins.top() + lineCorr), FPoint(m_width - m_textDistanceMargins.right() - lineCorr, m_textDistanceMargins.top() + lineCorr));
4959 	if (m_textDistanceMargins.bottom() + lineCorr!=0.0)
4960 		p->drawSharpLine(FPoint(m_textDistanceMargins.left() + lineCorr, m_height - m_textDistanceMargins.bottom() - lineCorr), FPoint(m_width - m_textDistanceMargins.right() - lineCorr, m_height - m_textDistanceMargins.bottom() - lineCorr));
4961 	while (curCol < m_columns)
4962 	{
4963 		colLeft=(colWidth + m_columnGap) * curCol + m_textDistanceMargins.left() + lineCorr;
4964 		if (colLeft != 0.0)
4965 			p->drawSharpLine(FPoint(colLeft, m_textDistanceMargins.top() + lineCorr), FPoint(colLeft, m_height - m_textDistanceMargins.bottom() - lineCorr));
4966 		if (colLeft + colWidth != m_width)
4967 			p->drawSharpLine(FPoint(colLeft + colWidth, m_textDistanceMargins.top() + lineCorr), FPoint(colLeft + colWidth, m_height - m_textDistanceMargins.bottom() - lineCorr));
4968 		++curCol;
4969 	}
4970 
4971 }
4972 
createInfoGroup(QFrame * infoGroup,QGridLayout * infoGroupLayout)4973 bool PageItem_TextFrame::createInfoGroup(QFrame *infoGroup, QGridLayout *infoGroupLayout)
4974 {
4975 	int Parag = 0;
4976 	int Words = 0;
4977 	int Chara = 0;
4978 	int ParagN = 0;
4979 	int WordsN = 0;
4980 	int CharaN = 0;
4981 
4982 	QLabel *infoCT = new QLabel(infoGroup);
4983 	QLabel *linesCT = new QLabel(infoGroup);
4984 	QLabel *linesT = new QLabel(infoGroup);
4985 	QLabel *parCT = new QLabel(infoGroup);
4986 	QLabel *parT = new QLabel(infoGroup);
4987 	QLabel *wordCT = new QLabel(infoGroup);
4988 	QLabel *wordT = new QLabel(infoGroup);
4989 	QLabel *charCT = new QLabel(infoGroup);
4990 	QLabel *charT = new QLabel(infoGroup);
4991 
4992 	if ((nextInChain() != nullptr) || (prevInChain() != nullptr))
4993 		infoCT->setText(tr("Linked Text"));
4994 	else
4995 		infoCT->setText(tr("Text Frame"));
4996 	infoGroupLayout->addWidget( infoCT, 0, 0, 1, 2, Qt::AlignCenter );
4997 
4998 	WordAndPara(this, &Words, &Parag, &Chara, &WordsN, &ParagN, &CharaN);
4999 	parCT->setText(tr("Paragraphs: "));
5000 	infoGroupLayout->addWidget( parCT, 1, 0, Qt::AlignRight );
5001 	if (ParagN != 0)
5002 		parT->setText(QString::number(Parag+ParagN)+" ("+QString::number(ParagN)+")");
5003 	else
5004 		parT->setText(QString::number(Parag));
5005 	infoGroupLayout->addWidget( parT, 1, 1 );
5006 
5007 	linesCT->setText(tr("Lines: "));
5008 	infoGroupLayout->addWidget( linesCT, 2, 0, Qt::AlignRight );
5009 	linesT->setText(QString::number(textLayout.lines()));
5010 	infoGroupLayout->addWidget( linesT, 2, 1 );
5011 
5012 
5013 	wordCT->setText(tr("Words: "));
5014 	infoGroupLayout->addWidget( wordCT, 3, 0, Qt::AlignRight );
5015 	if (WordsN != 0)
5016 		wordT->setText(QString::number(Words+WordsN)+" ("+QString::number(WordsN)+")");
5017 	else
5018 		wordT->setText(QString::number(Words));
5019 	infoGroupLayout->addWidget( wordT, 3, 1 );
5020 
5021 	charCT->setText(tr("Chars: "));
5022 	infoGroupLayout->addWidget(charCT, 4, 0, Qt::AlignRight );
5023 	if (CharaN != 0)
5024 		charT->setText(QString::number(Chara+CharaN)+" ("+QString::number(CharaN)+")");
5025 	else
5026 		charT->setText(QString::number(Chara));
5027 	infoGroupLayout->addWidget( charT, 4, 1 );
5028 	return true;
5029 }
5030 
toggleEditModeActions()5031 void PageItem_TextFrame::toggleEditModeActions()
5032 {
5033 	bool editMode = (m_Doc->appMode == modeEdit);
5034 	bool masterMode = m_Doc->masterPageMode();
5035 	m_Doc->scMW()->scrActions["insertMarkVariableText"]->setEnabled(editMode);
5036 	m_Doc->scMW()->scrActions["insertMarkAnchor"]->setEnabled(editMode && !masterMode);
5037 	m_Doc->scMW()->scrActions["insertMarkItem"]->setEnabled(editMode && !masterMode);
5038 	m_Doc->scMW()->scrActions["insertMark2Mark"]->setEnabled(editMode && !masterMode);
5039 	m_Doc->scMW()->scrActions["insertMarkNote"]->setEnabled(editMode && !masterMode && !isNoteFrame());
5040 	//	scrActions["insertMarkIndex"]->setEnabled(editMode && !masterMode);
5041 	bool enableEditMark = false;
5042 	if (editMode && (itemText.cursorPosition() < itemText.length()))
5043 	{
5044 		if (itemText.hasMark(itemText.cursorPosition()))
5045 		{
5046 			Mark* mrk = itemText.mark(itemText.cursorPosition());
5047 			//notes marks in note frames are not editable
5048 			if (!mrk->isType(MARKNoteFrameType))
5049 				enableEditMark = true;
5050 		}
5051 	}
5052 	m_Doc->scMW()->scrActions["editMark"]->setEnabled(enableEditMark);
5053 }
5054 
applicableActions(QStringList & actionList)5055 void PageItem_TextFrame::applicableActions(QStringList & actionList)
5056 {
5057 	actionList << "insertMarkVariableText";
5058 	if (!m_Doc->masterPageMode())
5059 		actionList << "insertMarkAnchor";
5060 	//notes frames are not simply text frames
5061 	if (isNoteFrame())
5062 		return;
5063 	actionList << "fileImportText";
5064 	actionList << "fileImportAppendText";
5065 	actionList << "toolsEditWithStoryEditor";
5066 	actionList << "insertSampleText";
5067 	actionList << "itemPDFIsAnnotation";
5068 	if (doc()->currentPage()->pageNameEmpty())
5069 		actionList << "itemPDFIsBookmark";
5070 	if (isAnnotation())
5071 	{
5072 		if ((annotation().Type() == 0) || (annotation().Type() == 1) || ((annotation().Type() > Annotation::Listbox) && (annotation().Type() < Annotation::RadioButton)))
5073 			actionList << "itemPDFAnnotationProps";
5074 		else
5075 			actionList << "itemPDFFieldProps";
5076 	}
5077 	if ((prevInChain() == nullptr) && (nextInChain() == nullptr))
5078 	{
5079 		actionList << "itemConvertToImageFrame";
5080 		actionList << "itemConvertToPolygon";
5081 	}
5082 	actionList << "itemConvertToOutlines";
5083 	if (textLayout.lines() != 0)
5084 	{
5085 		actionList << "editClearContents";
5086 		if ((this->nextInChain() == nullptr) && frameOverflows())
5087 			actionList << "editTruncateContents";
5088 		actionList << "itemAdjustFrameHeightToText";
5089 	}
5090 }
5091 
drawNoteIcon(ScPainter * p)5092 void PageItem_TextFrame::drawNoteIcon(ScPainter *p)
5093 {
5094 	p->save();
5095 	p->translate(0, 24);
5096 	p->scale(1, -1);
5097 	p->setFillMode(ScPainter::None);
5098 	p->setStrokeMode(ScPainter::Solid);
5099 	FPointArray chArr;
5100 	if (annotation().Icon() == Annotation::Icon_Note)
5101 	{
5102 		chArr.svgInit();
5103 		chArr.svgMoveTo(9, 18);
5104 		chArr.svgLineTo(4, 18);
5105 		chArr.svgCurveToCubic(4, 7, 4, 4, 6, 3);
5106 		chArr.svgLineTo(20, 3);
5107 		chArr.svgCurveToCubic(18, 4, 18, 7, 18, 18);
5108 		chArr.svgLineTo(17, 18);
5109 		p->setupPolygon(&chArr, false);
5110 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5111 		p->strokePath();
5112 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 1.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5113 		p->drawLine(QPointF(10, 16), QPointF(14, 21));
5114 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 1.85625, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5115 		chArr.resize(0);
5116 		chArr.svgInit();
5117 		chArr.svgMoveTo(15.07, 20.523);
5118 		chArr.svgCurveToCubic(15.07, 19.672, 14.379, 18.977, 13.523, 18.977);
5119 		chArr.svgCurveToCubic(12.672, 18.977, 11.977, 19.672, 11.977, 20.523);
5120 		chArr.svgCurveToCubic(11.977, 21.379, 12.672, 22.07, 13.523, 22.07);
5121 		chArr.svgCurveToCubic(14.379, 22.07, 15.07, 21.379, 15.07, 20.523);
5122 		chArr.svgClosePath();
5123 		p->setupPolygon(&chArr);
5124 		p->strokePath();
5125 		p->drawLine(QPointF(6.5, 13.5), QPointF(15.5, 13.5));
5126 		p->drawLine(QPointF(6.5, 10.5), QPointF(13.5, 10.5));
5127 		p->drawLine(QPointF(6.801, 7.5), QPointF(15.5, 7.5));
5128 		chArr.resize(0);
5129 		chArr.svgInit();
5130 		chArr.svgMoveTo(9, 19);
5131 		chArr.svgLineTo(4, 19);
5132 		chArr.svgCurveToCubic(4, 8, 4, 5, 6, 4);
5133 		chArr.svgLineTo(20, 4);
5134 		chArr.svgCurveToCubic(18, 5, 18, 8, 18, 19);
5135 		chArr.svgLineTo(17, 19);
5136 		p->setupPolygon(&chArr, false);
5137 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5138 		p->strokePath();
5139 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5140 		p->drawLine(QPointF(10, 17), QPointF(14, 22));
5141 		chArr.resize(0);
5142 		chArr.svgInit();
5143 		chArr.svgMoveTo(15.07, 21.523);
5144 		chArr.svgCurveToCubic(15.07, 20.672, 14.379, 19.977, 13.523, 19.977);
5145 		chArr.svgCurveToCubic(12.672, 19.977, 11.977, 20.672, 11.977, 21.523);
5146 		chArr.svgCurveToCubic(11.977, 22.379, 12.672, 23.07, 13.523, 23.07);
5147 		chArr.svgCurveToCubic(14.379, 23.07, 15.07, 22.379, 15.07, 21.523);
5148 		chArr.svgClosePath();
5149 		p->setupPolygon(&chArr);
5150 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1.85625, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5151 		p->strokePath();
5152 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5153 		p->drawLine(QPointF(6.5, 14.5), QPointF(15.5, 14.5));
5154 		p->drawLine(QPointF(6.5, 11.5), QPointF(13.5, 11.5));
5155 		p->drawLine(QPointF(6.801, 8.5), QPointF(15.5, 8.5));
5156 	}
5157 	else if (annotation().Icon() == Annotation::Icon_Comment)
5158 	{
5159 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5160 		chArr.svgInit();
5161 		chArr.parseSVG("M 8 20 L 16 20 C 18.363 20 20 18.215 20 16 L 20 13 C 20 10.785 18.363 9 16 9 L 13 9 L 8 3 L 8 9 C 5.637 9 4 10.785 4 13 L 4 16 C 4 18.215 5.637 20 8 20 Z");
5162 		p->setupPolygon(&chArr);
5163 		p->strokePath();
5164 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5165 		chArr.resize(0);
5166 		chArr.svgInit();
5167 		chArr.parseSVG("M 8 21 L 16 21 C 18.363 21 20 19.215 20 17 L 20 14 C 20 11.785 18.363 10 16 10 L 13 10 L 8 4 L 8 10 L 8 10 C 5.637 10 4 11.785 4 14 L 4 17 C 4 19.215 5.637 21 8 21 Z");
5168 		p->setupPolygon(&chArr);
5169 		p->strokePath();
5170 	}
5171 	else if (annotation().Icon() == Annotation::Icon_Key)
5172 	{
5173 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5174 		chArr.svgInit();
5175 		QString svg = "M 11.895 18.754 C 13.926 20.625 17.09 20.496 18.961 18.465 C 20.832 16.434 20.699 13.27 18.668 11.398 C 17.164 10.016 15.043 9.746 13.281 10.516";
5176 		svg += " L 12.473 9.324 L 11.281 10.078 L 9.547 8.664 L 9.008 6.496 L 7.059 6.059 L 6.34 4.121 L 5.543 3.668 L 3.375 4.207 L 2.938 6.156";
5177 		svg += " L 10.57 13.457 C 9.949 15.277 10.391 17.367 11.895 18.754 Z";
5178 		chArr.parseSVG(svg);
5179 		p->setupPolygon(&chArr);
5180 		p->strokePath();
5181 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 1.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5182 		chArr.resize(0);
5183 		chArr.svgInit();
5184 		svg = "M 16.059 15.586 C 16.523 15.078 17.316 15.043 17.824 15.512 C 18.332 15.98 18.363 16.77 17.895 17.277 C 17.43 17.785 16.637 17.816 16.129 17.352";
5185 		svg += " C 15.621 16.883 15.59 16.094 16.059 15.586 Z";
5186 		chArr.parseSVG(svg);
5187 		p->setupPolygon(&chArr);
5188 		p->strokePath();
5189 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5190 		chArr.resize(0);
5191 		chArr.svgInit();
5192 		svg = "M 11.895 19.754 C 13.926 21.625 17.09 21.496 18.961 19.465 C 20.832 17.434 20.699 14.27 18.668 12.398 C 17.164 11.016 15.043 10.746 13.281 11.516";
5193 		svg += " L 12.473 10.324 L 11.281 11.078 L 9.547 9.664 L 9.008 7.496 L 7.059 7.059 L 6.34 5.121 L 5.543 4.668 L 3.375 5.207 L 2.938 7.156";
5194 		svg += " L 10.57 14.457 C 9.949 16.277 10.391 18.367 11.895 19.754 Z";
5195 		chArr.parseSVG(svg);
5196 		p->setupPolygon(&chArr);
5197 		p->strokePath();
5198 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5199 		chArr.resize(0);
5200 		chArr.svgInit();
5201 		svg = "M 16.059 16.586 C 16.523 16.078 17.316 16.043 17.824 16.512 C 18.332 16.98 18.363 17.77 17.895 18.277";
5202 		svg += " C 17.43 18.785 16.637 18.816 16.129 18.352 C 15.621 17.883 15.59 17.094 16.059 16.586 Z";
5203 		chArr.parseSVG(svg);
5204 		p->setupPolygon(&chArr);
5205 		p->strokePath();
5206 	}
5207 	else if (annotation().Icon() == Annotation::Icon_Help)
5208 	{
5209 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5210 		chArr.svgInit();
5211 		QString svg = "M 8.289 16.488 C 8.824 17.828 10.043 18.773 11.473 18.965 C 12.902 19.156 14.328 18.559 15.195 17.406 C 16.062 16.254 16.242 14.723 15.664 13.398";
5212 		chArr.parseSVG(svg);
5213 		p->setupPolygon(&chArr, false);
5214 		p->strokePath();
5215 		chArr.resize(0);
5216 		chArr.svgInit();
5217 		svg = "M 12 8 C 12 12 16 11 16 15";
5218 		chArr.parseSVG(svg);
5219 		p->setupPolygon(&chArr, false);
5220 		p->strokePath();
5221 		chArr.resize(0);
5222 		chArr.svgInit();
5223 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 1.539286, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5224 		p->save();
5225 		p->translate(0, 24);
5226 		p->scale(1, -1);
5227 		svg = "M 12.684 20.891 C 12.473 21.258 12.004 21.395 11.629 21.196 C 11.254 20.992 11.105 20.531 11.297 20.149 C 11.488 19.77 11.945 19.61 12.332 19.789";
5228 		svg += " C 12.719 19.969 12.891 20.426 12.719 20.817";
5229 		chArr.parseSVG(svg);
5230 		p->setupPolygon(&chArr, false);
5231 		p->strokePath();
5232 		p->restore();
5233 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5234 		chArr.resize(0);
5235 		chArr.svgInit();
5236 		svg = "M 8.289 17.488 C 9.109 19.539 11.438 20.535 13.488 19.711 C 15.539 18.891 16.535 16.562 15.711 14.512 C 15.699 14.473 15.684 14.438 15.664 14.398";
5237 		chArr.parseSVG(svg);
5238 		p->setupPolygon(&chArr, false);
5239 		p->strokePath();
5240 		chArr.resize(0);
5241 		chArr.svgInit();
5242 		svg = "M 12 9 C 12 13 16 12 16 16";
5243 		chArr.parseSVG(svg);
5244 		p->setupPolygon(&chArr, false);
5245 		p->strokePath();
5246 		chArr.resize(0);
5247 		chArr.svgInit();
5248 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1.539286, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5249 		p->save();
5250 		p->translate(0, 24);
5251 		p->scale(1, -1);
5252 		svg = "M 12.684 19.891 C 12.473 20.258 12.004 20.395 11.629 20.195 C 11.254 19.992 11.105 19.531 11.297 19.149 C 11.488 18.77 11.945 18.61 12.332 18.789";
5253 		svg += " C 12.719 18.969 12.891 19.426 12.719 19.817";
5254 		chArr.parseSVG(svg);
5255 		p->setupPolygon(&chArr, false);
5256 		p->strokePath();
5257 		p->restore();
5258 	}
5259 	else if (annotation().Icon() == Annotation::Icon_NewParagraph)
5260 	{
5261 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5262 		chArr.svgInit();
5263 		p->save();
5264 		p->translate(0, 24);
5265 		p->scale(1, -1);
5266 		QString svg = "M 9.211 11.988 C 8.449 12.07 7.711 11.707 7.305 11.059 C 6.898 10.41 6.898 9.59 7.305 8.941 C 7.711 8.293 8.449 7.93 9.211 8.012";
5267 		chArr.parseSVG(svg);
5268 		p->setupPolygon(&chArr, false);
5269 		p->strokePath();
5270 		chArr.resize(0);
5271 		chArr.svgInit();
5272 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 1.004413, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5273 		svg = "M 18.07 11.511 L 15.113 10.014 L 12.199 11.602 L 12.711 8.323 L 10.301 6.045 L 13.574 5.517 L 14.996 2.522 L 16.512 5.474 L 19.801 5.899 L 17.461 8.252 L 18.07 11.511 Z";
5274 		chArr.parseSVG(svg);
5275 		p->setupPolygon(&chArr);
5276 		p->strokePath();
5277 		p->restore();
5278 		chArr.resize(0);
5279 		chArr.svgInit();
5280 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5281 		svg = "M 11 17 L 10 17 L 10 3";
5282 		chArr.parseSVG(svg);
5283 		p->setupPolygon(&chArr, false);
5284 		p->strokePath();
5285 		chArr.resize(0);
5286 		chArr.svgInit();
5287 		svg = "M 14 3 L 14 13";
5288 		chArr.parseSVG(svg);
5289 		p->setupPolygon(&chArr, false);
5290 		p->strokePath();
5291 		chArr.resize(0);
5292 		chArr.svgInit();
5293 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5294 		p->save();
5295 		p->translate(0, 24);
5296 		p->scale(1, -1);
5297 		svg = "M 9.211 10.988 C 8.109 11.105 7.125 10.309 7.012 9.211 C 6.895 8.109 7.691 7.125 8.789 7.012 C 8.93 6.996 9.07 6.996 9.211 7.012";
5298 		chArr.parseSVG(svg);
5299 		p->setupPolygon(&chArr, false);
5300 		p->strokePath();
5301 		chArr.resize(0);
5302 		chArr.svgInit();
5303 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 1.004413, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5304 		svg = "M 18.07 10.502 L 15.113 9.005 L 12.199 10.593 L 12.711 7.314 L 10.301 5.036 L 13.574 4.508 L 14.996 1.513 L 16.512 4.465 L 19.801 4.891 L 17.461 7.243 L 18.07 10.502 Z";
5305 		chArr.parseSVG(svg);
5306 		p->setupPolygon(&chArr);
5307 		p->strokePath();
5308 		p->restore();
5309 		chArr.resize(0);
5310 		chArr.svgInit();
5311 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5312 		svg = "M 11 18 L 10 18 L 10 4";
5313 		chArr.parseSVG(svg);
5314 		p->setupPolygon(&chArr, false);
5315 		p->strokePath();
5316 		chArr.resize(0);
5317 		chArr.svgInit();
5318 		svg = "M 14 4 L 14 14";
5319 		chArr.parseSVG(svg);
5320 		p->setupPolygon(&chArr, false);
5321 		p->strokePath();
5322 	}
5323 	else if (annotation().Icon() == Annotation::Icon_Paragraph)
5324 	{
5325 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5326 		chArr.svgInit();
5327 		QString svg = "M 15 3 L 15 18 L 11 18 L 11 3";
5328 		chArr.parseSVG(svg);
5329 		p->setupPolygon(&chArr, false);
5330 		p->strokePath();
5331 		chArr.resize(0);
5332 		chArr.svgInit();
5333 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5334 		p->save();
5335 		p->translate(0, 24);
5336 		p->scale(1, -1);
5337 		svg = "M 9.777 10.988 C 8.746 10.871 7.973 9.988 8 8.949 C 8.027 7.91 8.844 7.066 9.879 7.004";
5338 		chArr.parseSVG(svg);
5339 		p->setupPolygon(&chArr);
5340 		p->strokePath();
5341 		p->restore();
5342 		chArr.resize(0);
5343 		chArr.svgInit();
5344 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5345 		svg = "M 15 4 L 15 19 L 11 19 L 11 4";
5346 		chArr.parseSVG(svg);
5347 		p->setupPolygon(&chArr);
5348 		p->strokePath();
5349 		chArr.resize(0);
5350 		chArr.svgInit();
5351 		p->save();
5352 		p->translate(0, 24);
5353 		p->scale(1, -1);
5354 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 4, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5355 		svg = "M 9.777 9.988 C 8.746 9.871 7.973 8.988 8 7.949 C 8.027 6.91 8.844 6.066 9.879 6.004";
5356 		chArr.parseSVG(svg);
5357 		p->setupPolygon(&chArr);
5358 		p->strokePath();
5359 		p->restore();
5360 	}
5361 	else if (annotation().Icon() == Annotation::Icon_Insert)
5362 	{
5363 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5364 		p->drawLine(QPointF(9, 10), QPointF(17, 10));
5365 		p->drawLine(QPointF(12, 14.012), QPointF(20, 14));
5366 		p->drawLine(QPointF(12, 6.012), QPointF(20, 6.012));
5367 		chArr.svgInit();
5368 		QString svg = "M 4 12 L 6 10 L 4 8";
5369 		chArr.parseSVG(svg);
5370 		p->setupPolygon(&chArr, false);
5371 		p->strokePath();
5372 		chArr.resize(0);
5373 		chArr.svgInit();
5374 		p->drawLine(QPointF(4, 12), QPointF(4, 8));
5375 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5376 		p->drawLine(QPointF(12, 19.012), QPointF(20, 19));
5377 		p->drawLine(QPointF(12, 15.012), QPointF(20, 15));
5378 		p->drawLine(QPointF(12, 7.012), QPointF(20, 7.012));
5379 		svg = "M 4 13 L 6 11 L 4 9";
5380 		chArr.parseSVG(svg);
5381 		p->setupPolygon(&chArr, false);
5382 		p->strokePath();
5383 		p->drawLine(QPointF(4, 13), QPointF(4, 9));
5384 	}
5385 	else if (annotation().Icon() == Annotation::Icon_Cross)
5386 	{
5387 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5388 		p->drawLine(QPointF(18, 5), QPointF(6, 17));
5389 		p->drawLine(QPointF(6, 5), QPointF(18, 17));
5390 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5391 		p->drawLine(QPointF(18, 6), QPointF(6, 18));
5392 		p->drawLine(QPointF(6, 6), QPointF(18, 18));
5393 	}
5394 	else if (annotation().Icon() == Annotation::Icon_Circle)
5395 	{
5396 		p->setPen(QColor(qRound(255 * 0.533333), qRound(255 * 0.541176), qRound(255 * 0.521569)), 2.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5397 		chArr.svgInit();
5398 		QString svg = "M 19.5 11.5 C 19.5 7.359 16.141 4 12 4 C 7.859 4 4.5 7.359 4.5 11.5 C 4.5 15.641 7.859 19 12 19 C 16.141 19 19.5 15.641 19.5 11.5 Z";
5399 		chArr.parseSVG(svg);
5400 		p->setupPolygon(&chArr);
5401 		p->strokePath();
5402 		chArr.resize(0);
5403 		p->setPen(QColor(qRound(255 * 0.729412), qRound(255 * 0.741176), qRound(255 * 0.713725)), 2.5, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
5404 		chArr.svgInit();
5405 		svg = "M 19.5 12.5 C 19.5 8.359 16.141 5 12 5 C 7.859 5 4.5 8.359 4.5 12.5 C 4.5 16.641 7.859 20 12 20 C 16.141 20 19.5 16.641 19.5 12.5 Z";
5406 		chArr.parseSVG(svg);
5407 		p->setupPolygon(&chArr);
5408 		p->strokePath();
5409 	}
5410 	p->restore();
5411 }
5412 
5413 
setTextAnnotationOpen(bool open)5414 void PageItem_TextFrame::setTextAnnotationOpen(bool open)
5415 {
5416 	if (annotation().Type() == Annotation::Text)
5417 	{
5418 		if (open)
5419 		{
5420 			m_origAnnotPos = QRectF(xPos(), yPos(), width(), height());
5421 			setWidthHeight(265, 265, true);
5422 		}
5423 		else
5424 		{
5425 			setXYPos(m_origAnnotPos.x(), m_origAnnotPos.y(), true);
5426 			setWidthHeight(m_origAnnotPos.width(), m_origAnnotPos.height(), true);
5427 		}
5428 	}
5429 }
5430 
infoDescription() const5431 QString PageItem_TextFrame::infoDescription() const
5432 {
5433 	return QString();
5434 }
5435 
hasNoteMark(NotesStyle * NS)5436 bool PageItem_TextFrame::hasNoteMark(NotesStyle *NS)
5437 {
5438 	if (isNoteFrame())
5439 		return (asNoteFrame()->notesStyle() == NS);
5440 
5441 	if (NS == nullptr)
5442 	{
5443 		//find any mark
5444 		if (!m_notesFramesMap.isEmpty())
5445 			return true;
5446 		for (int i=firstInFrame(); i <= lastInFrame(); ++i)
5447 			if (itemText.hasMark(i))
5448 				return true;
5449 	}
5450 	else
5451 	{
5452 		for (int pos = firstInFrame(); pos <= lastInFrame(); ++pos)
5453 		{
5454 			if (itemText.hasMark(pos))
5455 			{
5456 				TextNote* note = itemText.mark(pos)->getNotePtr();
5457 				if (note != nullptr && (note->notesStyle() == NS))
5458 					return true;
5459 			}
5460 		}
5461 	}
5462 	return false;
5463 }
5464 
hasNoteFrame(NotesStyle * NS,bool inChain)5465 bool PageItem_TextFrame::hasNoteFrame(NotesStyle *NS, bool inChain)
5466 {
5467 	if (isNoteFrame())
5468 		return false;
5469 	if (m_notesFramesMap.isEmpty())
5470 		return false;
5471 	if (NS == nullptr)
5472 	{ //check if any notes are in frame or whole chain
5473 		if (!inChain)
5474 			return !m_notesFramesMap.isEmpty();
5475 		PageItem* item = this;
5476 		item = firstInChain();
5477 		while (item != nullptr)
5478 		{
5479 			if (item->asTextFrame()->hasNoteFrame(nullptr, false))
5480 				return true;
5481 			item = item->nextInChain();
5482 		}
5483 		return false;
5484 	}
5485 	PageItem* item = this;
5486 	if (inChain)
5487 		item = firstInChain();
5488 	while (item != nullptr)
5489 	{
5490 		NotesInFrameMap::iterator it = m_notesFramesMap.begin();
5491 		NotesInFrameMap::iterator end = m_notesFramesMap.end();
5492 		while (it != end)
5493 		{
5494 			if (it.key()->notesStyle() == NS)
5495 				return true;
5496 			++it;
5497 		}
5498 		if (!inChain)
5499 			break;
5500 		item = item->nextInChain();
5501 	}
5502 	return false;
5503 }
5504 
delAllNoteFrames(bool doUpdate)5505 void PageItem_TextFrame::delAllNoteFrames(bool doUpdate)
5506 {
5507 	int oldItemsCount = m_Doc->Items->count();
5508 
5509 	QList<PageItem_NoteFrame*> delList;
5510 	for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5511 	{
5512 		if (nF->notesList().isEmpty() && !nF->isAutoNoteFrame())
5513 			delList.append(nF);
5514 	}
5515 	while (!delList.isEmpty())
5516 	{
5517 		PageItem_NoteFrame* nF = delList.takeFirst();
5518 		m_Doc->delNoteFrame(nF);
5519 	}
5520 
5521 	//check if doc need update
5522 	if (doUpdate && (oldItemsCount != m_Doc->Items->count()))
5523 	{
5524 		m_Doc->changed();
5525 		m_Doc->regionsChanged()->update(QRectF());
5526 	}
5527 	m_Doc->setNotesChanged(true);
5528 }
5529 
selectedMark(bool onlySelection)5530 Mark* PageItem_TextFrame::selectedMark(bool onlySelection)
5531 { //return pointer to first mark in selected (or whole) text
5532 
5533 	bool omitNotes = true; //do not return notes marks (for searching notes use selectedNotesMark()
5534 	int start = 0;
5535 	int stop = 0;
5536 	if (onlySelection)
5537 	{
5538 		if (itemText.hasSelection())
5539 		{
5540 			//only selection
5541 			start = itemText.startOfSelection();
5542 			stop = start + itemText.selectionLength();
5543 		}
5544 		else //in edit mode only one char in cursor position
5545 		{
5546 			if (m_Doc->appMode == modeEdit)
5547 			{
5548 				//only char after cursor
5549 				start = itemText.cursorPosition();
5550 				stop = start + 1;
5551 				if (stop > itemText.length())
5552 					return nullptr;
5553 			}
5554 			else
5555 			{
5556 				//only frame
5557 				start = firstInFrame();
5558 				stop = lastInFrame();
5559 				if (start == stop)
5560 					return nullptr;
5561 			}
5562 		}
5563 	}
5564 	else //in whole text
5565 		stop = itemText.length();
5566 
5567 	for (int pos = start; pos < stop; ++pos)
5568 	{
5569 		if (itemText.hasMark(pos))
5570 		{
5571 			Mark* mark = itemText.mark(pos);
5572 			if (omitNotes && (mark->isType(MARKNoteMasterType) || mark->isType(MARKNoteFrameType)))
5573 				continue;
5574 			if (mark->isType(MARKBullNumType))
5575 				continue;
5576 			return mark;
5577 		}
5578 	}
5579 	return nullptr;
5580 }
5581 
selectedNoteMark(int & foundPos,bool onlySelection)5582 TextNote* PageItem_TextFrame::selectedNoteMark(int &foundPos, bool onlySelection)
5583 {
5584 	//return pointer to note from first mark found in text
5585 	int start = 0;
5586 	int stop = itemText.length();
5587 	if (onlySelection)
5588 	{
5589 		if (itemText.hasSelection())
5590 		{
5591 			start = itemText.startOfSelection();
5592 			stop = start + itemText.selectionLength();
5593 		}
5594 		else
5595 			return nullptr;
5596 	}
5597 	MarkType typ = isNoteFrame()? MARKNoteFrameType : MARKNoteMasterType;
5598 	for (int pos = start; pos < stop; ++pos)
5599 	{
5600 		if (itemText.hasMark(pos) && itemText.mark(pos)->isType(typ))
5601 		{
5602 			foundPos = pos;
5603 			return itemText.mark(pos)->getNotePtr();
5604 		}
5605 	}
5606 	return nullptr;
5607 }
5608 
selectedNoteMark(bool onlySelection)5609 TextNote* PageItem_TextFrame::selectedNoteMark(bool onlySelection)
5610 {
5611 	int dummy;
5612 	return selectedNoteMark(dummy, onlySelection);
5613 }
5614 
updateNotesFrames(QMap<int,Mark * > noteMarksPosMap)5615 NotesInFrameMap PageItem_TextFrame::updateNotesFrames(QMap<int, Mark*> noteMarksPosMap)
5616 {
5617 	NotesInFrameMap notesMap; // = m_notesFramesMap;
5618 	QMap<int, Mark*>::Iterator it = noteMarksPosMap.begin();
5619 	QMap<int, Mark*>::Iterator end = noteMarksPosMap.end();
5620 	PageItem* lastItem = this;
5621 	while (it != end)
5622 	{
5623 		if (it.key() <= lastInFrame())
5624 		{
5625 			Mark* mark = it.value();
5626 			mark->setItemPtr(this);
5627 			mark->setItemName(itemName());
5628 
5629 			TextNote* note = mark->getNotePtr();
5630 			Q_ASSERT(note != nullptr);
5631 			if (note == nullptr)
5632 			{
5633 				qWarning() << "note mark without valid note pointer";
5634 				note = m_Doc->newNote(m_Doc->m_docNotesStylesList.at(0));
5635 				note->setMasterMark(mark);
5636 				mark->setNotePtr(note);
5637 			}
5638 			NotesStyle* NS = note->notesStyle();
5639 			PageItem_NoteFrame* nF = nullptr;
5640 			if (NS->isEndNotes())
5641 				nF = m_Doc->endNoteFrame(NS, this);
5642 			else
5643 				nF = itemNoteFrame(NS);
5644 			if (nF == nullptr)
5645 			{
5646 				//creating new noteframe
5647 				if (NS->isEndNotes())
5648 				{
5649 					//create new endnotes frame
5650 					double x,y,w,h;
5651 					const ScPage* scP = m_Doc->page4EndNotes(NS, this);
5652 					x = scP->Margins.left() + m_Doc->rulerXoffset + scP->xOffset();
5653 					y = scP->Margins.top() + m_Doc->rulerYoffset + scP->yOffset();
5654 					w = scP->width() - scP->Margins.left() - scP->Margins.right();
5655 					h = calculateLineSpacing(itemText.defaultStyle(), this);
5656 					nF = m_Doc->createNoteFrame(note->notesStyle(), x, y, w, h, m_Doc->itemToolPrefs().shapeLineWidth, CommonStrings::None, m_Doc->itemToolPrefs().textFont);
5657 					switch (NS->range())
5658 					{ //insert pointer to endnoteframe into m_Doc->m_endNotesFramesMap
5659 						case NSRdocument:
5660 							m_Doc->setEndNoteFrame(nF, (void*) nullptr);
5661 							break;
5662 						case NSRstory:
5663 							m_Doc->setEndNoteFrame(nF, (void*) firstInChain());
5664 							break;
5665 						default:
5666 							qDebug() << "Deprecated range prohibited for end-notes";
5667 							Q_ASSERT(false);
5668 							break;
5669 					}
5670 				}
5671 				else
5672 					//create new footnotes frame for that text frame
5673 					nF = m_Doc->createNoteFrame(this, note->notesStyle(), m_Doc->DocItems.indexOf(lastItem));
5674 				//insert in map noteframe with empty list of notes
5675 				m_notesFramesMap.insert(nF, QList<TextNote*>());
5676 				m_Doc->setNotesChanged(true);
5677 			}
5678 			else if (NS->isEndNotes())
5679 			{//check endnotes frame proper page
5680 				const ScPage* scP = m_Doc->page4EndNotes(NS, this);
5681 				if (scP->pageNr() != nF->OwnPage)
5682 				{
5683 					double x,y;
5684 					x = scP->Margins.left() + m_Doc->rulerXoffset + scP->xOffset();
5685 					y = scP->Margins.top() + m_Doc->rulerYoffset + scP->yOffset();
5686 					if ((scP->pageNr() != nF->OwnPage) || (nF->xPos() > (x + scP->width())) || nF->yPos() > (y + scP->height()))
5687 					{
5688 						undoManager->setUndoEnabled(false);
5689 						nF->setXYPos(x,y);
5690 						undoManager->setUndoEnabled(true);
5691 					}
5692 				}
5693 			}
5694 			QList<TextNote*> nList;//list of notes in current noteFrame
5695 			nList = notesMap.value(nF);
5696 			if (!nList.contains(note))
5697 			{
5698 				nList.append(note);
5699 				notesMap.insert(nF, nList);
5700 			}
5701 			if (!nF->isEndNotesFrame())
5702 				lastItem = nF;
5703 		}
5704 		else
5705 			break;
5706 		++it;
5707 	}
5708 	return notesMap;
5709 }
5710 
updateNotesMarks(NotesInFrameMap notesMap)5711 void PageItem_TextFrame::updateNotesMarks(NotesInFrameMap notesMap)
5712 {
5713 	bool docWasChanged = false;
5714 
5715 //	QList<PageItem_NoteFrame*> curr_footNotesList;
5716 //	QList<PageItem_NoteFrame*> old_footNotesList;
5717 //	QList<PageItem_NoteFrame*> curr_endNotesList;
5718 //	QList<PageItem_NoteFrame*> old_endNotesList;
5719 
5720 
5721 //	for (PageItem_NoteFrame* nF : notesMap.keys())
5722 //	{
5723 //		if (nF->isEndNotesFrame())
5724 //			curr_endNotesList.append(nF);
5725 //		else if (!notesMap.value(nF).isEmpty())
5726 //			curr_footNotesList.append(nF);
5727 //	}
5728 //	for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5729 //	{
5730 //		if (nF->isEndNotesFrame())
5731 //			old_endNotesList.append(nF);
5732 //		else
5733 //			old_footNotesList.append(nF);
5734 //	}
5735 //	//check for endnotes marks change in current frame
5736 //	for (PageItem_NoteFrame* nF : old_endNotesList)
5737 //	{
5738 //		if (nF->deleteIt)
5739 //		{
5740 //			m_Doc->delNoteFrame(nF,true);
5741 //			docWasChanged = true;
5742 //		}
5743 //		else if (!notesMap.contains(nF) || (m_notesFramesMap.value(nF) != notesMap.value(nF)))
5744 //		{
5745 //			m_Doc->endNoteFrameChanged(nF);
5746 //			docWasChanged = true;
5747 //		}
5748 //	}
5749 	//check if some notes frames are not used anymore
5750 	for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5751 	{
5752 		if (nF->deleteIt || (nF->isAutoNoteFrame() && !notesMap.keys().contains(nF)))
5753 		{
5754 			m_Doc->delNoteFrame(nF, true);
5755 			m_notesFramesMap.remove(nF);
5756 			notesMap.remove(nF);
5757 			docWasChanged = true;
5758 		}
5759 		else
5760 		{
5761 			QList<TextNote*> nList = notesMap.value(nF);
5762 			if (nList != nF->notesList() || m_Doc->notesChanged())
5763 			{
5764 				nF->updateNotes(nList, (!nF->isEndNotesFrame() && !nF->notesList().isEmpty()));
5765 				docWasChanged = true;
5766 			}
5767 		}
5768 	}
5769 	if (m_notesFramesMap != notesMap)
5770 	{
5771 		docWasChanged = true;
5772 		for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5773 		{
5774 			if (notesMap.contains(nF))
5775 			{
5776 				m_notesFramesMap.insert(nF, notesMap.value(nF));
5777 				notesMap.remove(nF);
5778 			}
5779 			else if (nF->isAutoNoteFrame() || nF->isEndNotesFrame())
5780 				m_notesFramesMap.remove(nF);
5781 		}
5782 		m_notesFramesMap.unite(notesMap);
5783 	}
5784 	if (docWasChanged)
5785 	{
5786 		m_Doc->flag_restartMarksRenumbering = true;
5787 		m_Doc->setNotesChanged(true);
5788 	}
5789 }
5790 
notesFramesLayout()5791 void PageItem_TextFrame::notesFramesLayout()
5792 {
5793 	for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5794 	{
5795 		if (nF == nullptr)
5796 			continue;
5797 		if (nF->deleteIt)
5798 			continue;
5799 		if (nF->isEndNotesFrame() && m_Doc->flag_updateEndNotes)
5800 			m_Doc->updateEndNotesFrameContent(nF);
5801 		nF->invalid = true;
5802 		nF->layout();
5803 	}
5804 }
5805 
removeMarksFromText(bool doUndo)5806 int PageItem_TextFrame::removeMarksFromText(bool doUndo)
5807 {
5808 	int num = 0;
5809 	if (!isNoteFrame())
5810 	{
5811 		TextNote* note = selectedNoteMark(true);
5812 		while (note != nullptr)
5813 		{
5814 			if (doUndo && UndoManager::undoEnabled())
5815 				m_Doc->setUndoDelNote(note);
5816 			if (note->isEndNote())
5817 				m_Doc->flag_updateEndNotes = true;
5818 			m_Doc->deleteNote(note);
5819 			note = selectedNoteMark(true);
5820 			++num;
5821 		}
5822 	}
5823 
5824 	Mark* mrk = selectedMark(true);
5825 	while (mrk != nullptr)
5826 	{
5827 		if (!mrk->isType(MARKBullNumType))
5828 		{
5829 			if (doUndo)
5830 				m_Doc->setUndoDelMark(mrk);
5831 			m_Doc->eraseMark(mrk, true, this);
5832 			++num;
5833 		}
5834 		mrk = selectedMark(true);
5835 	}
5836 	if (num > 0)
5837 	{
5838 		m_Doc->changed();
5839 		m_Doc->regionsChanged()->update(QRectF());
5840 		m_Doc->scMW()->marksManager->updateListView();
5841 	}
5842 	return num;
5843 }
5844 
itemNoteFrame(NotesStyle * nStyle)5845 PageItem_NoteFrame *PageItem_TextFrame::itemNoteFrame(NotesStyle *nStyle)
5846 {
5847 	for (PageItem_NoteFrame* nF : m_notesFramesMap.keys())
5848 		if (nF->notesStyle() == nStyle)
5849 			return nF;
5850 	return nullptr;
5851 }
5852 
setNoteFrame(PageItem_NoteFrame * nF)5853 void PageItem_TextFrame::setNoteFrame(PageItem_NoteFrame *nF)
5854 {
5855 	 m_notesFramesMap.insert(nF, nF->notesList());
5856 }
5857 
setMaxY(double y)5858 void PageItem_TextFrame::setMaxY(double y)
5859 {
5860 	if (y == -1)
5861 		maxY = 0.0;
5862 	else
5863 		maxY = qMax(y, maxY);
5864 }
5865 
setTextFrameHeight()5866 void PageItem_TextFrame::setTextFrameHeight()
5867 {
5868 	if (textLayout.lines() <= 0)
5869 		return;
5870 
5871 	if (m_nextBox == nullptr) // Vertical alignment is not used inside a text chain
5872 	{
5873 		textLayout.box()->moveTo(textLayout.box()->x(), 0);
5874 		double newHeight = textLayout.box()->naturalHeight();
5875 		newHeight += m_textDistanceMargins.bottom();
5876 
5877 		UndoTransaction undoTransaction;
5878 		if (UndoManager::undoEnabled())
5879 		{
5880 			QString unitSuffix = unitGetStrFromIndex(m_Doc->unitIndex());
5881 			int unitPrecision  = unitGetPrecisionFromIndex(m_Doc->unitIndex());
5882 			double unitRatio   = m_Doc->unitRatio();
5883 			QString owString  = QString::number(oldWidth * unitRatio, 'f', unitPrecision) + " " + unitSuffix;
5884 			QString ohString  = QString::number(oldHeight * unitRatio, 'f', unitPrecision) + " " + unitSuffix;
5885 			QString nwString  = QString::number(m_width * unitRatio, 'f', unitPrecision) + " " + unitSuffix;
5886 			QString nhString  = QString::number(m_height * unitRatio, 'f', unitPrecision) + " " + unitSuffix;
5887 			QString tooltip   = QString(Um::ResizeFromTo).arg(owString, ohString, nwString, nhString);
5888 			undoTransaction = undoManager->beginTransaction(Um::Selection, Um::ITextFrame, Um::Resize, tooltip, Um::IResize);
5889 		}
5890 
5891 		setHeight(newHeight);
5892 
5893 		if (undoTransaction)
5894 			undoTransaction.commit();
5895 	}
5896 
5897 	updateClip();
5898 	invalid = true;
5899 	m_Doc->changed();
5900 	m_Doc->regionsChanged()->update(QRect());
5901 }
5902