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 ¤t, 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