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 	copyright            : (C) 2005 by Craig Bradney
9 	email                : cbradney@zip.com.au
10 ***************************************************************************/
11 
12 /***************************************************************************
13 *                                                                         *
14 *   This program is free software; you can redistribute it and/or modify  *
15 *   it under the terms of the GNU General Public License as published by  *
16 *   the Free Software Foundation; either version 2 of the License, or     *
17 *   (at your option) any later version.                                   *
18 *                                                                         *
19 ***************************************************************************/
20 
21 #include "sclimits.h"
22 #include "scribusdoc.h"
23 #include "selection.h"
24 #include <QDebug>
25 
Selection(QObject * parent)26 Selection::Selection(QObject* parent) :
27 	QObject(parent),
28 	m_isGUISelection(false),
29 	m_delaySignals(0),
30 	m_sigSelectionChanged(false)
31 {
32 	m_groupX   = m_groupY   = m_groupW   = m_groupH   = 0;
33 	m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
34 }
35 
Selection(QObject * parent,bool guiSelection)36 Selection::Selection(QObject* parent, bool guiSelection) :
37 	QObject(parent),
38 	m_isGUISelection(guiSelection),
39 	m_delaySignals(0),
40 	m_sigSelectionChanged(false)
41 {
42 	m_groupX   = m_groupY   = m_groupW   = m_groupH   = 0;
43 	m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
44 }
45 
Selection(const Selection & other)46 Selection::Selection(const Selection& other) :
47 	QObject(other.parent()),
48 	m_SelList(other.m_SelList),
49 	// We do not copy m_isGUISelection as :
50 	// 1) copy ctor is used for temporary selections
51 	// 2) having two GUI selections for same doc should really be avoided
52 	m_isGUISelection(false),
53 	// We do not copy m_delaySignals as that can potentially
54 	// cause much trouble balancing delaySignalOff/On right
55 	m_delaySignals(0),
56 	m_sigSelectionChanged(other.m_sigSelectionChanged)
57 {
58 	if (m_isGUISelection && !m_SelList.isEmpty())
59 	{
60 		m_SelList[0]->connectToGUI();
61 		m_SelList[0]->emitAllToGUI();
62 		m_SelList[0]->setSelected(true);
63 		emit selectionChanged();
64 	}
65 	m_groupX = other.m_groupX;
66 	m_groupY = other.m_groupY;
67 	m_groupW = other.m_groupW;
68 	m_groupH = other.m_groupH;
69 	m_visualGX = other.m_visualGX;
70 	m_visualGY = other.m_visualGY;
71 	m_visualGW = other.m_visualGW;
72 	m_visualGH = other.m_visualGH;
73 }
74 
operator =(const Selection & other)75 Selection& Selection::operator=(const Selection &other)
76 {
77 	if (&other == this)
78 		return *this;
79 	if (m_isGUISelection)
80 	{
81 		SelectionList::Iterator itend = m_SelList.end();
82 		for (SelectionList::Iterator it = m_SelList.begin(); it != itend; ++it)
83 			(*it)->setSelected(false);
84 	}
85 	m_SelList = other.m_SelList;
86 	// Do not copy m_isGUISelection for consistency with cpy ctor
87 	/* m_isGUISelection = other.m_isGUISelection; */
88 	// We do not copy m_delaySignals as that can potentially
89 	// cause much trouble balancing delaySignalOff/On right
90 	m_sigSelectionChanged = other.m_sigSelectionChanged;
91 	if (m_isGUISelection && !m_SelList.isEmpty())
92 	{
93 		m_SelList[0]->connectToGUI();
94 		m_SelList[0]->emitAllToGUI();
95 		m_SelList[0]->setSelected(true);
96 		emit selectionChanged();
97 	}
98 	return *this;
99 }
100 
copy(Selection & other,bool emptyOther)101 void Selection::copy(Selection& other, bool emptyOther)
102 {
103 	if (&other == this)
104 		return;
105 	if (m_isGUISelection)
106 	{
107 		SelectionList::Iterator itend = m_SelList.end();
108 		for (SelectionList::Iterator it = m_SelList.begin(); it != itend; ++it)
109 			(*it)->setSelected(false);
110 	}
111 	m_SelList = other.m_SelList;
112 	if (m_isGUISelection)
113 		m_sigSelectionChanged = true;
114 	if (emptyOther)
115 		other.clear();
116 	sendSignals();
117 }
118 
~Selection()119 Selection::~Selection()
120 {
121 }
122 
clear()123 bool Selection::clear()
124 {
125 	if (!m_SelList.isEmpty())
126 	{
127 		SelectionList::Iterator itend = m_SelList.end();
128 		SelectionList::Iterator it = m_SelList.begin();
129 		while (it != itend)
130 		{
131 			(*it)->isSingleSel=false;
132 			if (m_isGUISelection)
133 			{
134 				(*it)->setSelected(false);
135 				(*it)->disconnectFromGUI();
136 			}
137 			++it;
138 		}
139 	}
140 	m_SelList.clear();
141 	m_sigSelectionChanged = true;
142 	sendSignals();
143 	return true;
144 }
145 
connectItemToGUI()146 bool Selection::connectItemToGUI()
147 {
148 	if (!m_isGUISelection || m_SelList.isEmpty())
149 		return false;
150 
151 	QPointer<PageItem> pi = m_SelList.first();
152 	//Quick check to see if the pointer is nullptr, if its nullptr, we should remove it from the list now
153 	while (pi.isNull())
154 	{
155 		m_SelList.removeAll(pi);
156 		if (m_SelList.isEmpty())
157 			break;
158 		pi = m_SelList.first();
159 	}
160 
161 	if (pi.isNull())
162 		return false;
163 
164 	bool ret = pi->connectToGUI();
165 	pi->emitAllToGUI();
166 	m_sigSelectionChanged = true;
167 
168 	sendSignals(false);
169 	return ret;
170 }
171 
disconnectAllItemsFromGUI()172 bool Selection::disconnectAllItemsFromGUI()
173 {
174 	if (!m_isGUISelection || m_SelList.isEmpty())
175 		return false;
176 	SelectionList::Iterator it2end = m_SelList.end();
177 	SelectionList::Iterator it2 = m_SelList.begin();
178 	while (it2 != it2end)
179 	{
180 		(*it2)->disconnectFromGUI();
181 		++it2;
182 	}
183 	return true;
184 }
185 
addItem(PageItem * item,bool)186 bool Selection::addItem(PageItem *item, bool /*ignoreGUI*/)
187 {
188 	if (item == nullptr)
189 		return false;
190 	bool listWasEmpty = m_SelList.isEmpty();
191 	if (listWasEmpty || !m_SelList.contains(item))
192 	{
193 		m_SelList.append(item);
194 		if (m_isGUISelection)
195 		{
196 			item->setSelected(true);
197 			m_sigSelectionChanged = true;
198 		}
199 		sendSignals();
200 		return true;
201 	}
202 	return false;
203 }
204 
addItems(const QList<PageItem * > items)205 bool Selection::addItems(const QList<PageItem *> items)
206 {
207 	if (items.isEmpty())
208 		return false;
209 
210 	QList< QPointer<PageItem> > toAdd;
211 	toAdd.reserve(items.count());
212 	for (int i = 0; i < items.count(); ++i)
213 	{
214 		PageItem* item = items.at(i);
215 		if (m_SelList.contains(item))
216 			continue;
217 		toAdd.append(item);
218 		item->setSelected(true);
219 	}
220 
221 	if (toAdd.count() <= 0)
222 		return false;
223 
224 	m_SelList.append(toAdd);
225 	if (m_isGUISelection)
226 		m_sigSelectionChanged = true;
227 	sendSignals();
228 	return true;
229 }
230 
prependItem(PageItem * item)231 bool Selection::prependItem(PageItem *item)
232 {
233 	if (item == nullptr)
234 		return false;
235 	if (m_SelList.contains(item))
236 		return false;
237 
238 	if (m_isGUISelection && !m_SelList.isEmpty())
239 		m_SelList[0]->disconnectFromGUI();
240 	m_SelList.prepend(item);
241 	if (m_isGUISelection)
242 	{
243 		item->setSelected(true);
244 		m_sigSelectionChanged = true;
245 	}
246 	sendSignals();
247 	return true;
248 }
249 
itemAt_(int index)250 PageItem *Selection::itemAt_(int index)
251 {
252 	if (!m_SelList.isEmpty() && index < m_SelList.count())
253 	{
254 		QPointer<PageItem> pi = m_SelList[index];
255 		//If not nullptr return it, otherwise remove from the list and return nullptr
256 		if (!pi.isNull())
257 			return pi;
258 		m_SelList.removeAt(index);
259 	}
260 	return nullptr;
261 }
262 
items() const263 QList<PageItem*> Selection::items() const
264 {
265 	QList<PageItem*> selectedItems;
266 	for (int i = 0; i < m_SelList.count(); ++i)
267 	{
268 		QPointer<PageItem> pi = m_SelList.at(i);
269 		if (pi.isNull())
270 			continue;
271 		selectedItems.append(pi.data());
272 	}
273 	return selectedItems;
274 }
275 
removeFirst()276 bool Selection::removeFirst()
277 {
278 	if (!m_SelList.isEmpty())
279 	{
280 		if (m_isGUISelection && m_SelList.first())
281 			m_SelList.first()->setSelected(false);
282 		removeItem(m_SelList.first());
283 		if (m_SelList.isEmpty())
284 			return true;
285 		if (m_isGUISelection)
286 			m_sigSelectionChanged = true;
287 		sendSignals();
288 	}
289 	return false;
290 }
291 
removeItem(PageItem * item)292 bool Selection::removeItem(PageItem *item)
293 {
294 	if (m_SelList.isEmpty() || !m_SelList.contains(item))
295 		return false;
296 
297 	bool removeOk = (m_SelList.removeAll(item) == 1);
298 	if (removeOk)
299 	{
300 		if (m_isGUISelection)
301 		{
302 			item->setSelected(false);
303 			item->disconnectFromGUI();
304 		}
305 		item->isSingleSel = false;
306 	}
307 
308 	if (m_isGUISelection)
309 	{
310 		m_sigSelectionChanged = true;
311 		sendSignals();
312 	}
313 	return removeOk;
314 }
315 
removeItemsOfLayer(int layedID)316 bool Selection::removeItemsOfLayer(int layedID)
317 {
318 	if (m_SelList.isEmpty())
319 		return false;
320 
321 	int oldSelCount = m_SelList.count();
322 
323 	delaySignalsOn();
324 
325 	int selIndex = 0;
326 	while (selIndex < m_SelList.count())
327 	{
328 		QPointer<PageItem> pi = m_SelList.at(selIndex);
329 		if (pi.isNull())
330 		{
331 			removeItem(itemAt(selIndex));
332 			continue;
333 		}
334 
335 		if (pi->m_layerID != layedID)
336 		{
337 			++selIndex;
338 			continue;
339 		}
340 
341 		removeItem(itemAt(selIndex));
342 	}
343 
344 	delaySignalsOff();
345 
346 	bool itemsRemoved = (oldSelCount != m_SelList.count());
347 	return itemsRemoved;
348 }
349 
replaceItem(PageItem * oldItem,PageItem * newItem)350 void Selection::replaceItem(PageItem* oldItem, PageItem* newItem)
351 {
352 	delaySignalsOn();
353 
354 	int itemIndex = findItem(oldItem);
355 	if (itemIndex >= 0)
356 		m_SelList.replace(itemIndex, newItem);
357 
358 	delaySignalsOff();
359 }
360 
takeItem(int itemIndex)361 PageItem* Selection::takeItem(int itemIndex)
362 {
363 	if (m_SelList.isEmpty() || itemIndex >= m_SelList.count())
364 		return nullptr;
365 
366 	PageItem *item =  m_SelList[itemIndex];
367 	bool removeOk = (m_SelList.removeAll(item) == 1);
368 	if (removeOk)
369 	{
370 		item->isSingleSel = false;
371 		if (m_isGUISelection)
372 		{
373 			item->setSelected(false);
374 			m_sigSelectionChanged = true;
375 			if (itemIndex == 0)
376 				item->disconnectFromGUI();
377 		}
378 		sendSignals();
379 		return item;
380 	}
381 	return nullptr;
382 }
383 
getSelectedItemsByName() const384 QStringList Selection::getSelectedItemsByName() const
385 {
386 	QStringList names;
387 	SelectionList::ConstIterator itend = m_SelList.end();
388 	for (auto it = m_SelList.begin(); it != itend ; ++it)
389 		names.append((*it)->itemName());
390 	return names;
391 }
392 
width() const393 double Selection::width() const
394 {
395 	if (m_SelList.isEmpty())
396 		return 0.0;
397 	double minX =  std::numeric_limits<double>::max();
398 	double maxX = -std::numeric_limits<double>::max();
399 	double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
400 
401 	SelectionList::ConstIterator itend = m_SelList.end();
402 	for (auto it = m_SelList.begin(); it != itend ; ++it)
403 	{
404 		(*it)->getBoundingRect(&x1, &y1, &x2, &y2);
405 		if (x1 < minX)
406 			minX = x1;
407 		if (x2 > maxX)
408 			maxX = x2;
409 	}
410 	return maxX - minX;
411 }
412 
height() const413 double Selection::height() const
414 {
415 	if (m_SelList.isEmpty())
416 		return 0.0;
417 	double minY =  std::numeric_limits<double>::max();
418 	double maxY = -std::numeric_limits<double>::max();
419 	double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
420 
421 	SelectionList::ConstIterator itend = m_SelList.end();
422 	for (auto it = m_SelList.begin(); it != itend ; ++it)
423 	{
424 		(*it)->getBoundingRect(&x1, &y1, &x2, &y2);
425 		if (y1 < minY)
426 			minY = y1;
427 		if (y2 > maxY)
428 			maxY = y2;
429 	}
430 	return maxY - minY;
431 }
432 
setGroupRect()433 void Selection::setGroupRect()
434 {
435 	PageItem *currItem;
436 	int selectedItemCount = count();
437 	if (selectedItemCount == 0)
438 	{
439 		m_groupX   = m_groupY   = m_groupW   = m_groupH   = 0;
440 		m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
441 		return;
442 	}
443 	double minx  =  std::numeric_limits<double>::max();
444 	double miny  =  std::numeric_limits<double>::max();
445 	double maxx  = -std::numeric_limits<double>::max();
446 	double maxy  = -std::numeric_limits<double>::max();
447 	double vminx =  std::numeric_limits<double>::max();
448 	double vminy =  std::numeric_limits<double>::max();
449 	double vmaxx = -std::numeric_limits<double>::max();
450 	double vmaxy = -std::numeric_limits<double>::max();
451 
452 	for (int i = 0; i < selectedItemCount; ++i)
453 	{
454 		currItem = itemAt(i);
455 		if (currItem->rotation() != 0.0)
456 		{
457 			QRectF itRect(currItem->getBoundingRect());
458 			minx = qMin(minx, itRect.x());
459 			miny = qMin(miny, itRect.y());
460 			maxx = qMax(maxx, itRect.right());
461 			maxy = qMax(maxy, itRect.bottom());
462 
463 			// Same for visual
464 			QRectF itVisualRect(currItem->getVisualBoundingRect());
465 			vminx = qMin(vminx, itVisualRect.x());
466 			vminy = qMin(vminy, itVisualRect.y());
467 			vmaxx = qMax(vmaxx, itVisualRect.right());
468 			vmaxy = qMax(vmaxy, itVisualRect.bottom());
469 		}
470 		else
471 		{
472 			minx = qMin(minx, currItem->xPos());
473 			miny = qMin(miny, currItem->yPos());
474 			maxx = qMax(maxx, currItem->xPos() + currItem->width());
475 			maxy = qMax(maxy, currItem->yPos() + currItem->height());
476 
477 			vminx = qMin(vminx, currItem->visualXPos());
478 			vminy = qMin(vminy, currItem->visualYPos());
479 			vmaxx = qMax(vmaxx, currItem->visualXPos() + currItem->visualWidth());
480 			vmaxy = qMax(vmaxy, currItem->visualYPos() + currItem->visualHeight());
481 		}
482 	}
483 	m_groupX = minx;
484 	m_groupY = miny;
485 	m_groupW = maxx - minx;
486 	m_groupH = maxy - miny;
487 
488 	m_visualGX = vminx;
489 	m_visualGY = vminy;
490 	m_visualGW = vmaxx - vminx;
491 	m_visualGH = vmaxy - vminy;
492 }
493 
getGroupRect(double * x,double * y,double * w,double * h)494 void Selection::getGroupRect(double *x, double *y, double *w, double *h)
495 {
496 	setGroupRect();
497 	*x = m_groupX;
498 	*y = m_groupY;
499 	*w = m_groupW;
500 	*h = m_groupH;
501 }
502 
getGroupRect()503 QRectF Selection::getGroupRect()
504 {
505 	double x, y, w, h;
506 	getGroupRect(&x, &y, &w, &h);
507 	return QRectF(x, y, w, h);
508 }
509 
getVisualGroupRect(double * x,double * y,double * w,double * h)510 void Selection::getVisualGroupRect(double * x, double * y, double * w, double * h)
511 {
512 	setGroupRect();
513 	*x = m_visualGX;
514 	*y = m_visualGY;
515 	*w = m_visualGW;
516 	*h = m_visualGH;
517 }
518 
getVisualGroupRect()519 QRectF Selection::getVisualGroupRect()
520 {
521 	double x, y, w, h;
522 	getGroupRect(&x, &y, &w, &h);
523 	return QRectF(x, y, w, h);
524 }
525 
containsItemType(PageItem::ItemType type) const526 bool Selection::containsItemType(PageItem::ItemType type) const
527 {
528 	if (m_SelList.isEmpty())
529 		return false;
530 	SelectionList::ConstIterator it = m_SelList.begin();
531 	SelectionList::ConstIterator itend = m_SelList.end();
532 	for (; it != itend; ++it)
533 	{
534 		if ((*it)->itemType() == type)
535 			return true;
536 	}
537 	return false;
538 }
539 
itemsAreSameType() const540 bool Selection::itemsAreSameType() const
541 {
542 	//CB Putting count=1 before isempty test as its probably the most likely, given our view code.
543 	if (m_SelList.count() == 1)
544 		return true;
545 	if (m_SelList.isEmpty())
546 		return false;
547 	SelectionList::ConstIterator it = m_SelList.begin();
548 	SelectionList::ConstIterator itend = m_SelList.end();
549 	PageItem::ItemType itemType = (*it)->itemType();
550 	for ( ; it != itend ; ++it)
551 	{
552 		if ((*it)->isGroup())		// ignore GroupControl items
553 			continue;
554 		if ((*it)->itemType() != itemType)
555 			return false;
556 	}
557 	return true;
558 }
559 
itemsAreOnSamePage() const560 bool Selection::itemsAreOnSamePage() const
561 {
562 	//CB Putting count=1 before isempty test as its probably the most likely, given our view code.
563 	if (m_SelList.count() == 1)
564 		return true;
565 	if (m_SelList.isEmpty())
566 		return false;
567 	auto it = m_SelList.begin();
568 	auto itend = m_SelList.end();
569 	auto ownPage = (*it)->OwnPage;
570 	for ( ; it != itend ; ++it)
571 	{
572 		if ((*it)->OwnPage != ownPage)
573 			return false;
574 	}
575 	return true;
576 }
577 
objectsLayer() const578 int Selection::objectsLayer() const
579 {
580 	if (m_SelList.isEmpty())
581 		return -1;
582 	int layerID = m_SelList.at(0)->m_layerID;
583 	for (int i = 1; i < m_SelList.count(); ++i)
584 	{
585 		if (m_SelList.at(i)->m_layerID != layerID)
586 		{
587 			layerID = -1;
588 			break;
589 		}
590 	}
591 	return layerID;
592 }
593 
objectsHaveSameParent() const594 bool Selection::objectsHaveSameParent() const
595 {
596 	int selectedItemCount = m_SelList.count();
597 	if (selectedItemCount <= 1)
598 		return true;
599 
600 	bool haveSameParent = true;
601 	const PageItem *firstItem = itemAt(0);
602 	for (int i = 1; i < selectedItemCount; ++i)
603 	{
604 		if (itemAt(i)->Parent != firstItem->Parent)
605 		{
606 			haveSameParent = false;
607 			break;
608 		}
609 	}
610 	return haveSameParent;
611 }
612 
signalsDelayed()613 bool Selection::signalsDelayed()
614 {
615 	return (m_isGUISelection && (m_delaySignals > 0));
616 }
617 
delaySignalsOn()618 void Selection::delaySignalsOn()
619 {
620 	++m_delaySignals;
621 }
622 
delaySignalsOff()623 void Selection::delaySignalsOff()
624 {
625 	--m_delaySignals;
626 	if (m_delaySignals <= 0)
627 		sendSignals();
628 }
629 
sendSignals(bool guiConnect)630 void Selection::sendSignals(bool guiConnect)
631 {
632 	if (m_isGUISelection && (m_delaySignals <= 0))
633 	{
634 		setGroupRect();
635 		// JG - We should probably add an m_sigSelectionChanged here
636 		// to avoid multiple connectItemToGUI() if sendSignals() is called
637 		// several times successively (but does that happen?)
638 		if (guiConnect /*&& m_sigSelectionChanged*/)
639 			connectItemToGUI();
640 		if (m_sigSelectionChanged)
641 			emit selectionChanged();
642 		m_sigSelectionChanged = false;
643 	}
644 }
645 
646 
647