1 
2 #include <cmath>
3 
4 #include "pageitem_noteframe.h"
5 
6 #include "appmodes.h"
7 #include "pageitem.h"
8 #include "pageitem_textframe.h"
9 
10 #include "scribusdoc.h"
11 #include "undomanager.h"
12 #include "util.h"
13 #include "util_text.h"
14 
15 #include "text/boxes.h"
16 
PageItem_NoteFrame(NotesStyle * nStyle,ScribusDoc * doc,double x,double y,double w,double h,double w2,const QString & fill,const QString & outline)17 PageItem_NoteFrame::PageItem_NoteFrame(NotesStyle *nStyle, ScribusDoc *doc, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline)
18 	: PageItem_TextFrame(doc, x, y, w, h, w2, fill, outline)
19 {
20 	m_nstyle = nStyle;
21 	itemText.clear();
22 
23 	m_itemName = generateUniqueCopyName(nStyle->isEndNotes() ? tr("Endnote frame ") + m_nstyle->name() : tr("Footnote frame ") + m_nstyle->name(), false);
24 	AutoName = false; //endnotes frame will saved with name
25 	setUName(m_itemName);
26 
27 	//set default style for note frame
28 	ParagraphStyle newStyle;
29 	if (nStyle->notesParStyle().isEmpty() || (nStyle->notesParStyle() == tr("No Style")))
30 		newStyle.setParent(m_Doc->paragraphStyles()[0].name()); // set default doc style
31 	else
32 		newStyle.setParent(nStyle->notesParStyle());
33 	itemText.blockSignals(true);
34 	itemText.setDefaultStyle(newStyle);
35 	itemText.blockSignals(false);
36 
37 	m_textFlowMode = TextFlowUsesFrameShape;
38 	setColumns(1);
39 
40 	m_SizeVLocked = m_nstyle->isAutoNotesHeight();
41 	m_SizeHLocked = m_nstyle->isAutoNotesWidth();
42 	m_SizeLocked = m_nstyle->isAutoNotesHeight() && m_nstyle->isAutoNotesWidth();
43 }
44 
PageItem_NoteFrame(ScribusDoc * doc,double x,double y,double w,double h,double w2,const QString & fill,const QString & outline)45 PageItem_NoteFrame::PageItem_NoteFrame(ScribusDoc *doc, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline)
46 	: PageItem_TextFrame(doc, x, y, w, h, w2, fill, outline)
47 {
48 	m_textFlowMode = TextFlowUsesFrameShape;
49 }
50 
PageItem_NoteFrame(PageItem_TextFrame * inFrame,NotesStyle * nStyle)51 PageItem_NoteFrame::PageItem_NoteFrame(PageItem_TextFrame* inFrame, NotesStyle *nStyle) : PageItem_TextFrame(inFrame->doc(),inFrame->xPos(), inFrame->yPos(),inFrame->width(), inFrame->height(),inFrame->lineWidth(), inFrame->fillColor(), inFrame->lineColor())
52 {
53 	m_nstyle = nStyle;
54 	m_masterFrame = inFrame;
55 
56 	m_itemName = generateUniqueCopyName(nStyle->isEndNotes() ? tr("Endnote frame ") + m_nstyle->name() : tr("Footnote frame ") + m_nstyle->name(), false);
57 	AutoName = false;
58 	setUName(m_itemName);
59 
60 	//set default style for note frame
61 	ParagraphStyle newStyle;
62 	if (nStyle->notesParStyle().isEmpty() || (nStyle->notesParStyle() == tr("No Style")))
63 	{
64 		if (nStyle->isEndNotes())
65 			//set default doc style
66 			newStyle.setParent(m_Doc->paragraphStyles()[0].name());
67 		else
68 		{
69 			newStyle.setParent(m_masterFrame->itemText.defaultStyle().parent());
70 			newStyle.applyStyle(m_masterFrame->currentStyle());
71 		}
72 	}
73 	else
74 		newStyle.setParent(nStyle->notesParStyle());
75 	itemText.blockSignals(true);
76 	itemText.setDefaultStyle(newStyle);
77 	itemText.blockSignals(false);
78 
79 	double frameHeight = calculateLineSpacing(newStyle, this);
80 	if (frameHeight == 0.0 && !m_nstyle->isAutoNotesHeight())
81 		frameHeight = newStyle.charStyle().fontSize()/10;
82 	m_height = oldHeight = frameHeight;
83 	oldWidth = m_width;
84 	oldRot = m_rotation;
85 	oldXpos = m_xPos;
86 	m_yPos = oldYpos = m_masterFrame->yPos() + m_masterFrame->height();
87 
88 	m_textFlowMode = TextFlowUsesFrameShape;
89 	setColumns(1);
90 
91 	if (m_nstyle->isAutoWeldNotesFrames() && (m_masterFrame != nullptr))
92 	{
93 		addWelded(m_masterFrame);
94 		m_masterFrame->addWelded(this);
95 		m_masterFrame->setWeldPoint(0, m_masterFrame->height(), this);
96 		setWeldPoint(0,0, m_masterFrame);
97 	}
98 	if (m_nstyle->isAutoNotesHeight())
99 		m_SizeVLocked = true;
100 	else
101 		m_SizeVLocked = false;
102 	if (m_nstyle->isAutoNotesWidth())
103 		m_SizeHLocked = true;
104 	else
105 		m_SizeHLocked = false;
106 	if (m_nstyle->isAutoNotesHeight() && m_nstyle->isAutoNotesWidth())
107 		m_SizeLocked = true;
108 	else
109 		m_SizeLocked = false;
110 }
111 
getNamedResources(ResourceCollection & lists) const112 void PageItem_NoteFrame::getNamedResources(ResourceCollection& lists) const
113 {
114 	PageItem_TextFrame::getNamedResources(lists);
115 
116 	if (m_nstyle)
117 		lists.collectNoteStyle(m_nstyle->name());
118 }
119 
replaceNamedResources(ResourceCollection & newNames)120 void PageItem_NoteFrame::replaceNamedResources(ResourceCollection& newNames)
121 {
122 	PageItem_TextFrame::replaceNamedResources(newNames);
123 
124 	if (m_nstyle)
125 	{
126 		const auto& rsrcNoteStyles = newNames.noteStyles();
127 		auto it = rsrcNoteStyles.find(m_nstyle->name());
128 		if ((it != rsrcNoteStyles.end()) && (*it != m_nstyle->name()))
129 		{
130 			NotesStyle* newStyle = m_Doc->getNotesStyle(*it);
131 			setNoteStyle(newStyle);
132 		}
133 	}
134 }
135 
setNoteStyle(NotesStyle * nStyle,PageItem_TextFrame * master)136 void PageItem_NoteFrame::setNoteStyle(NotesStyle *nStyle, PageItem_TextFrame* master)
137 {
138 	m_nstyle = nStyle;
139 	if (master != nullptr)
140 		m_masterFrame = master;
141 	itemText.clear();
142 
143 	m_itemName = generateUniqueCopyName(m_nstyle->isEndNotes() ? "Endnote frame " + m_nstyle->name() : "Footnote frame " + m_nstyle->name(), false);
144 	setUName(m_itemName);
145 
146 	//set default style for note frame
147 	ParagraphStyle newStyle;
148 	if (nStyle->notesParStyle().isEmpty() || (nStyle->notesParStyle() == tr("No Style")))
149 	{
150 		if (nStyle->isEndNotes() || (m_masterFrame == nullptr))
151 		{
152 			//set default doc style
153 			newStyle.setParent(m_Doc->paragraphStyles()[0].name());
154 		}
155 		else if (master != nullptr)
156 		{
157 			newStyle.setParent(m_masterFrame->itemText.defaultStyle().parent());
158 			newStyle.applyStyle(m_masterFrame->currentStyle());
159 		}
160 	}
161 	else
162 		newStyle.setParent(nStyle->notesParStyle());
163 	itemText.blockSignals(true);
164 	itemText.setDefaultStyle(newStyle);
165 	itemText.blockSignals(false);
166 
167 	if (m_nstyle->isAutoNotesHeight())
168 		m_SizeVLocked = true;
169 	else
170 		m_SizeVLocked = false;
171 	if (m_nstyle->isAutoNotesWidth())
172 		m_SizeHLocked = true;
173 	else
174 		m_SizeHLocked = false;
175 	if (m_nstyle->isAutoNotesHeight() && m_nstyle->isAutoNotesWidth())
176 		m_SizeLocked = true;
177 	else
178 		m_SizeLocked = false;
179 }
180 
layout()181 void PageItem_NoteFrame::layout()
182 {
183 	if (!invalid || m_notes.isEmpty())
184 		return;
185 	if (!m_Doc->flag_layoutNotesFrames)
186 		return;
187 	if (itemText.length() == 0)
188 		return;
189 	if ((m_masterFrame != nullptr) && m_masterFrame->invalid)
190 		return;
191 
192 	//while layouting notes frames undo should be disabled
193 	UndoManager::instance()->setUndoEnabled(false);
194 
195 	if (m_nstyle->isAutoNotesWidth() && (m_width != m_masterFrame->width()))
196 	{
197 		oldWidth = m_width = m_masterFrame->width();
198 		updateClip();
199 	}
200 
201 	if ((m_Doc->appMode == modeEdit) && isSelected())
202 		updateNotesText();
203 
204 	PageItem_TextFrame::layout();
205 	int oldH = m_height;
206 	if (notesStyle()->isAutoNotesHeight())
207 	{
208 		if (frameOverflows())
209 		{
210 			// Increase height while text don`t fit in frame
211 			double maxH = m_Doc->currentPage()->height() - m_yPos;
212 			if (maxH <= 0)
213 				maxH = m_Doc->currentPage()->height();
214 			while (frameOverflows())
215 			{
216 				oldHeight = m_height += 8;
217 				updateClip(false);
218 				invalid = true;
219 				PageItem_TextFrame::layout();
220 				if (m_height >= maxH)
221 					break;
222 			}
223 		}
224 		textLayout.box()->moveTo(textLayout.box()->x(), 0);
225 		oldHeight = m_height = textLayout.box()->naturalHeight() + m_textDistanceMargins.bottom();
226 		updateConstants();
227 		updateClip();
228 		invalid = true;
229 		PageItem_TextFrame::layout();
230 	}
231 	if (oldH != height())
232 	{
233 		if (masterFrame() != nullptr)
234 		{
235 			foreach(PageItem_NoteFrame* nF, masterFrame()->notesFramesList())
236 				nF->invalid = true;
237 		}
238 	}
239 	invalid = false;
240 	m_Doc->regionsChanged()->update(getBoundingRect());
241 	UndoManager::instance()->setUndoEnabled(true);
242 }
243 
insertNote(TextNote * note)244 void PageItem_NoteFrame::insertNote(TextNote *note)
245 {
246 	Mark* mrk = note->noteMark();
247 	if (mrk == nullptr)
248 	{
249 		mrk = m_Doc->newMark();
250 		mrk->setType(MARKNoteFrameType);
251 		QString label = "NoteFrameMark_" + notesStyle()->name();
252 		if (notesStyle()->range() == NSRstory)
253 			label += " in " + note->masterMark()->getItemPtr()->firstInChain()->itemName();
254 		mrk->label = label + "_" + note->numString();
255 		mrk->setNotePtr(note);
256 		getUniqueName(mrk->label, m_Doc->marksLabelsList(MARKNoteFrameType), "_");
257 		note->setNoteMark(mrk);
258 	}
259 	mrk->setItemPtr(this);
260 	mrk->setString(notesStyle()->prefix() + note->numString() + note->notesStyle()->suffix());
261 
262 	StoryText story(m_Doc);
263 	if (!note->saxedText().isEmpty())
264 		story = desaxeString(m_Doc, note->saxedText());
265 	story.insertMark(mrk, 0);
266 	story.setDefaultStyle(itemText.defaultStyle());
267 //	story.applyCharStyle(0, story.length(), itemText.charStyle());
268 	if (itemText.length() > 0)
269 		itemText.insertChars(itemText.length(), SpecialChars::PARSEP);
270 	itemText.insert(itemText.length(), story);
271 }
272 
updateNotes(const QList<TextNote * > & nList,bool clear)273 void PageItem_NoteFrame::updateNotes(const QList<TextNote*>& nList, bool clear)
274 {
275 	if (nList == m_notes && !clear)
276 		return;
277 	UndoManager::instance()->setUndoEnabled(false);
278 	m_Doc->setNotesChanged(true);
279 	//itemText.blockSignals(true);
280 
281 	if (clear)
282 	{
283 		itemText.selectAll();
284 		deleteSelectedTextFromFrame();
285 		m_notes = nList;
286 		for (int a=0; a < m_notes.count(); ++a)
287 			insertNote(m_notes.at(a));
288 	}
289 	else
290 	{
291 		//just insert new notes into frame notes list
292 		int count = nList.count();
293 		if (count > 0)
294 		{
295 			for (int i=0; i< count; ++i)
296 			{
297 				TextNote* note = nList.at(i);
298 				if (!m_notes.contains(note))
299 				{
300 					m_notes.append(note);
301 					insertNote(note);
302 				}
303 			}
304 		}
305 	}
306 	UndoManager::instance()->setUndoEnabled(true);
307 	//itemText.blockSignals(false);
308 	invalid = true;
309 }
310 
updateNotesText()311 void PageItem_NoteFrame::updateNotesText()
312 {
313 	//read texts from notes frame and copy it to note`s data
314 	if (m_notes.isEmpty() || (itemText.length() == 0))
315 		return;
316 
317 	int oldSelStart = itemText.startOfSelection();
318 	int oldSelLen = itemText.selectionLength();
319 	int pos = 0;
320 	int startPos = 0;
321 	TextNote *note = nullptr;
322 	Mark* prevMrk = nullptr;
323 	while (pos < itemText.length())
324 	{
325 		if (itemText.hasMark(pos))
326 		{
327 			Mark* mark = itemText.mark(pos);
328 			if  (mark->isType(MARKNoteFrameType))
329 			{
330 				if (prevMrk != nullptr)
331 				{
332 					note = prevMrk->getNotePtr();
333 					if (note != nullptr)
334 					{
335 						int offset = 0;
336 						if (itemText.text(pos-1) == SpecialChars::PARSEP)
337 							++offset;
338 						int len = pos - startPos -offset;
339 						if (len <= 0)
340 							note->clearSaxedText();
341 						else
342 						{
343 							note->setSaxedText(getItemTextSaxed(startPos, len));
344 							note->textLen = len;
345 						}
346 						itemText.deselectAll();
347 					}
348 				}
349 				prevMrk = mark;
350 				startPos = pos +1;
351 			}
352 		}
353 		++pos;
354 	}
355 	if (prevMrk != nullptr)
356 	{
357 		note = prevMrk->getNotePtr();
358 		Q_ASSERT(note != nullptr);
359 		if (startPos != pos)
360 		{
361 			note->setSaxedText(getItemTextSaxed(startPos, pos - startPos));
362 			note->textLen = pos - startPos;
363 		}
364 		else //empty note text (only note marker)
365 			note->clearSaxedText();
366 	}
367 	if (oldSelLen > 0)
368 		itemText.select(oldSelStart, oldSelLen);
369 }
370 
restoreDeleteNoteText(SimpleState * state,bool isUndo)371 void PageItem_NoteFrame::restoreDeleteNoteText(SimpleState *state, bool isUndo)
372 {
373 	PageItem::restoreDeleteFrameText(state, isUndo);
374 	updateNotesText();
375 }
376 
restoreDeleteNoteParagraph(SimpleState * state,bool isUndo)377 void PageItem_NoteFrame::restoreDeleteNoteParagraph(SimpleState *state, bool isUndo)
378 {
379 	PageItem::restoreDeleteFrameParagraph(state, isUndo);
380 	updateNotesText();
381 }
382 
restoreInsertNoteText(SimpleState * state,bool isUndo)383 void PageItem_NoteFrame::restoreInsertNoteText(SimpleState *state, bool isUndo)
384 {
385 	PageItem::restoreInsertFrameText(state, isUndo);
386 	updateNotesText();
387 }
388 
restoreInsertNoteParagraph(SimpleState * state,bool isUndo)389 void PageItem_NoteFrame::restoreInsertNoteParagraph(SimpleState *state, bool isUndo)
390 {
391 	PageItem::restoreInsertFrameParagraph(state, isUndo);
392 	updateNotesText();
393 }
394 
unWeld(bool doUndo)395 void PageItem_NoteFrame::unWeld(bool doUndo)
396 {
397 	if (doUndo)
398 		PageItem::unWeld();
399 	else
400 	{
401 		for (int a = 0 ; a < weldList.count(); a++)
402 		{
403 			WeldingInfo wInf = weldList.at(a);
404 			PageItem *pIt = wInf.weldItem;
405 			for (int b = 0 ; b < pIt->weldList.count(); b++)
406 			{
407 				WeldingInfo wInf2 = pIt->weldList.at(b);
408 				PageItem *pIt2 = wInf2.weldItem;
409 				if (pIt2 == this)
410 				{
411 					pIt->weldList.removeAt(b);
412 					break;
413 				}
414 			}
415 		}
416 		weldList.clear();
417 	}
418 }
419 
findNoteCpos(TextNote * note)420 int PageItem_NoteFrame::findNoteCpos(TextNote* note)
421 {
422 	//find position of note in note`s frame
423 	if (itemText.length() == 0)
424 		return -1;
425 	for (int pos=0; pos < itemText.length(); ++pos)
426 	{
427 		Mark* mark = itemText.mark(pos);
428 		if (itemText.hasMark(pos) && mark->isType(MARKNoteFrameType))
429 		{
430 			if (mark->getNotePtr() == note)
431 				return (pos);
432 		}
433 	}
434 	return -1;
435 }
436